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 // [Bug 120627] the text on the Y axis is reversed when export ppt file to PDF format 7179 { 7180 long nOffsetX = rGlyphs[i+1].m_aPos.X() - rGlyphs[i].m_aPos.X(); 7181 long nOffsetY = rGlyphs[i+1].m_aPos.Y() - rGlyphs[i].m_aPos.Y(); 7182 nXOffset += (int)sqrt(double(nOffsetX*nOffsetX + nOffsetY*nOffsetY)); 7183 } 7184 if( ! rGlyphs[i].m_nGlyphId ) 7185 continue; 7186 7187 aDeltaPos = rRotScale.transform( aDeltaPos ); 7188 7189 Matrix3 aMat; 7190 if( fSkewB != 0.0 || fSkewA != 0.0 ) 7191 aMat.skew( fSkewA, fSkewB ); 7192 aMat.scale( fTempXScale, fYScale ); 7193 aMat.rotate( fAngle+fDeltaAngle ); 7194 aMat.translate( aCurPos.X()+aDeltaPos.X(), aCurPos.Y()+aDeltaPos.Y() ); 7195 aMat.append( m_aPages.back(), rLine ); 7196 rLine.append( " Tm" ); 7197 if( i == 0 || rGlyphs[i-1].m_nMappedFontId != rGlyphs[i].m_nMappedFontId ) 7198 { 7199 rLine.append( " /F" ); 7200 rLine.append( rGlyphs[i].m_nMappedFontId ); 7201 rLine.append( ' ' ); 7202 m_aPages.back().appendMappedLength( nFontHeight, rLine, true ); 7203 rLine.append( " Tf" ); 7204 } 7205 rLine.append( "<" ); 7206 appendHex( rGlyphs[i].m_nMappedGlyphId, rLine ); 7207 rLine.append( ">Tj\n" ); 7208 } 7209 } 7210 7211 void PDFWriterImpl::drawHorizontalGlyphs( 7212 const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs, 7213 OStringBuffer& rLine, 7214 const Point& rAlignOffset, 7215 double fAngle, 7216 double fXScale, 7217 double fSkew, 7218 sal_Int32 nFontHeight, 7219 sal_Int32 nPixelFontHeight 7220 ) 7221 { 7222 // horizontal (= normal) case 7223 7224 // fill in run end indices 7225 // end is marked by index of the first glyph of the next run 7226 // a run is marked by same mapped font id and same Y position 7227 std::vector< sal_uInt32 > aRunEnds; 7228 aRunEnds.reserve( rGlyphs.size() ); 7229 for( size_t i = 1; i < rGlyphs.size(); i++ ) 7230 { 7231 if( rGlyphs[i].m_nMappedFontId != rGlyphs[i-1].m_nMappedFontId || 7232 rGlyphs[i].m_aPos.Y() != rGlyphs[i-1].m_aPos.Y() ) 7233 { 7234 aRunEnds.push_back(i); 7235 } 7236 } 7237 // last run ends at last glyph 7238 aRunEnds.push_back( rGlyphs.size() ); 7239 7240 // loop over runs of the same font 7241 sal_uInt32 nBeginRun = 0; 7242 for( size_t nRun = 0; nRun < aRunEnds.size(); nRun++ ) 7243 { 7244 // setup text matrix 7245 Point aCurPos = rGlyphs[nBeginRun].m_aPos; 7246 // back transformation to current coordinate system 7247 aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos ); 7248 aCurPos += rAlignOffset; 7249 // the first run can be set with "Td" operator 7250 // subsequent use of that operator would move 7251 // the texline matrix relative to what was set before 7252 // making use of that would drive us into rounding issues 7253 Matrix3 aMat; 7254 if( nRun == 0 && fAngle == 0.0 && fXScale == 1.0 && fSkew == 0.0 ) 7255 { 7256 m_aPages.back().appendPoint( aCurPos, rLine, false ); 7257 rLine.append( " Td " ); 7258 } 7259 else 7260 { 7261 if( fSkew != 0.0 ) 7262 aMat.skew( 0.0, fSkew ); 7263 aMat.scale( fXScale, 1.0 ); 7264 aMat.rotate( fAngle ); 7265 aMat.translate( aCurPos.X(), aCurPos.Y() ); 7266 aMat.append( m_aPages.back(), rLine ); 7267 rLine.append( " Tm\n" ); 7268 } 7269 // set up correct font 7270 rLine.append( "/F" ); 7271 rLine.append( rGlyphs[nBeginRun].m_nMappedFontId ); 7272 rLine.append( ' ' ); 7273 m_aPages.back().appendMappedLength( nFontHeight, rLine, true ); 7274 rLine.append( " Tf" ); 7275 7276 // output glyphs using Tj or TJ 7277 OStringBuffer aKernedLine( 256 ), aUnkernedLine( 256 ); 7278 aKernedLine.append( "[<" ); 7279 aUnkernedLine.append( '<' ); 7280 appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aKernedLine ); 7281 appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aUnkernedLine ); 7282 7283 aMat.invert(); 7284 bool bNeedKern = false; 7285 for( sal_uInt32 nPos = nBeginRun+1; nPos < aRunEnds[nRun]; nPos++ ) 7286 { 7287 appendHex( rGlyphs[nPos].m_nMappedGlyphId, aUnkernedLine ); 7288 // check if default glyph positioning is sufficient 7289 const Point aThisPos = aMat.transform( rGlyphs[nPos].m_aPos ); 7290 const Point aPrevPos = aMat.transform( rGlyphs[nPos-1].m_aPos ); 7291 double fAdvance = aThisPos.X() - aPrevPos.X(); 7292 fAdvance *= 1000.0 / nPixelFontHeight; 7293 const sal_Int32 nAdjustment = (sal_Int32)(rGlyphs[nPos-1].m_nNativeWidth - fAdvance + 0.5); 7294 if( nAdjustment != 0 ) 7295 { 7296 // apply individual glyph positioning 7297 bNeedKern = true; 7298 aKernedLine.append( ">" ); 7299 aKernedLine.append( nAdjustment ); 7300 aKernedLine.append( "<" ); 7301 } 7302 appendHex( rGlyphs[nPos].m_nMappedGlyphId, aKernedLine ); 7303 } 7304 aKernedLine.append( ">]TJ\n" ); 7305 aUnkernedLine.append( ">Tj\n" ); 7306 rLine.append( bNeedKern ? aKernedLine : aUnkernedLine ); 7307 7308 // set beginning of next run 7309 nBeginRun = aRunEnds[nRun]; 7310 } 7311 } 7312 7313 void PDFWriterImpl::drawLayout( SalLayout& rLayout, const String& rText, bool bTextLines ) 7314 { 7315 // relief takes precedence over shadow (see outdev3.cxx) 7316 if( m_aCurrentPDFState.m_aFont.GetRelief() != RELIEF_NONE ) 7317 { 7318 drawRelief( rLayout, rText, bTextLines ); 7319 return; 7320 } 7321 else if( m_aCurrentPDFState.m_aFont.IsShadow() ) 7322 drawShadow( rLayout, rText, bTextLines ); 7323 7324 OStringBuffer aLine( 512 ); 7325 7326 const int nMaxGlyphs = 256; 7327 7328 sal_GlyphId pGlyphs[nMaxGlyphs]; 7329 sal_Int32 pGlyphWidths[nMaxGlyphs]; 7330 sal_uInt8 pMappedGlyphs[nMaxGlyphs]; 7331 sal_Int32 pMappedFontObjects[nMaxGlyphs]; 7332 std::vector<sal_Ucs> aUnicodes; 7333 aUnicodes.reserve( nMaxGlyphs ); 7334 sal_Int32 pUnicodesPerGlyph[nMaxGlyphs]; 7335 int pCharPosAry[nMaxGlyphs]; 7336 sal_Int32 nAdvanceWidths[nMaxGlyphs]; 7337 const ImplFontData* pFallbackFonts[nMaxGlyphs]; 7338 bool bVertical = m_aCurrentPDFState.m_aFont.IsVertical(); 7339 int nGlyphs; 7340 int nIndex = 0; 7341 int nMinCharPos = 0, nMaxCharPos = rText.Len()-1; 7342 double fXScale = 1.0; 7343 double fSkew = 0.0; 7344 sal_Int32 nPixelFontHeight = m_pReferenceDevice->mpFontEntry->maFontSelData.mnHeight; 7345 TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign(); 7346 7347 // transform font height back to current units 7348 // note: the layout calculates in outdevs device pixel !! 7349 sal_Int32 nFontHeight = m_pReferenceDevice->ImplDevicePixelToLogicHeight( nPixelFontHeight ); 7350 if( m_aCurrentPDFState.m_aFont.GetWidth() ) 7351 { 7352 Font aFont( m_aCurrentPDFState.m_aFont ); 7353 aFont.SetWidth( 0 ); 7354 FontMetric aMetric = m_pReferenceDevice->GetFontMetric( aFont ); 7355 if( aMetric.GetWidth() != m_aCurrentPDFState.m_aFont.GetWidth() ) 7356 { 7357 fXScale = 7358 (double)m_aCurrentPDFState.m_aFont.GetWidth() / 7359 (double)aMetric.GetWidth(); 7360 } 7361 // force state before GetFontMetric 7362 m_pReferenceDevice->ImplNewFont(); 7363 } 7364 7365 // perform artificial italics if necessary 7366 if( ( m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_NORMAL || 7367 m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_OBLIQUE ) && 7368 !( m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetSlant() == ITALIC_NORMAL || 7369 m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetSlant() == ITALIC_OBLIQUE ) 7370 ) 7371 { 7372 fSkew = M_PI/12.0; 7373 } 7374 7375 // if the mapmode is distorted we need to adjust for that also 7376 if( m_aCurrentPDFState.m_aMapMode.GetScaleX() != m_aCurrentPDFState.m_aMapMode.GetScaleY() ) 7377 { 7378 fXScale *= double(m_aCurrentPDFState.m_aMapMode.GetScaleX()) / double(m_aCurrentPDFState.m_aMapMode.GetScaleY()); 7379 } 7380 7381 int nAngle = m_aCurrentPDFState.m_aFont.GetOrientation(); 7382 // normalize angles 7383 while( nAngle < 0 ) 7384 nAngle += 3600; 7385 nAngle = nAngle % 3600; 7386 double fAngle = (double)nAngle * M_PI / 1800.0; 7387 7388 Matrix3 aRotScale; 7389 aRotScale.scale( fXScale, 1.0 ); 7390 if( fAngle != 0.0 ) 7391 aRotScale.rotate( -fAngle ); 7392 7393 bool bPop = false; 7394 bool bABold = false; 7395 // artificial bold necessary ? 7396 if( m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetWeight() <= WEIGHT_MEDIUM && 7397 m_pReferenceDevice->mpFontEntry->maFontSelData.GetWeight() > WEIGHT_MEDIUM ) 7398 { 7399 if( ! bPop ) 7400 aLine.append( "q " ); 7401 bPop = true; 7402 bABold = true; 7403 } 7404 // setup text colors (if necessary) 7405 Color aStrokeColor( COL_TRANSPARENT ); 7406 Color aNonStrokeColor( COL_TRANSPARENT ); 7407 7408 if( m_aCurrentPDFState.m_aFont.IsOutline() ) 7409 { 7410 aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor(); 7411 aNonStrokeColor = Color( COL_WHITE ); 7412 } 7413 else 7414 aNonStrokeColor = m_aCurrentPDFState.m_aFont.GetColor(); 7415 if( bABold ) 7416 aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor(); 7417 7418 if( aStrokeColor != Color( COL_TRANSPARENT ) && aStrokeColor != m_aCurrentPDFState.m_aLineColor ) 7419 { 7420 if( ! bPop ) 7421 aLine.append( "q " ); 7422 bPop = true; 7423 appendStrokingColor( aStrokeColor, aLine ); 7424 aLine.append( "\n" ); 7425 } 7426 if( aNonStrokeColor != Color( COL_TRANSPARENT ) && aNonStrokeColor != m_aCurrentPDFState.m_aFillColor ) 7427 { 7428 if( ! bPop ) 7429 aLine.append( "q " ); 7430 bPop = true; 7431 appendNonStrokingColor( aNonStrokeColor, aLine ); 7432 aLine.append( "\n" ); 7433 } 7434 7435 // begin text object 7436 aLine.append( "BT\n" ); 7437 // outline attribute ? 7438 if( m_aCurrentPDFState.m_aFont.IsOutline() || bABold ) 7439 { 7440 // set correct text mode, set stroke width 7441 aLine.append( "2 Tr " ); // fill, then stroke 7442 7443 if( m_aCurrentPDFState.m_aFont.IsOutline() ) 7444 { 7445 // unclear what to do in case of outline and artificial bold 7446 // for the time being outline wins 7447 aLine.append( "0.25 w \n" ); 7448 } 7449 else 7450 { 7451 double fW = (double)m_aCurrentPDFState.m_aFont.GetHeight() / 30.0; 7452 m_aPages.back().appendMappedLength( fW, aLine ); 7453 aLine.append ( " w\n" ); 7454 } 7455 } 7456 7457 FontMetric aRefDevFontMetric = m_pReferenceDevice->GetFontMetric(); 7458 7459 // collect the glyphs into a single array 7460 const int nTmpMaxGlyphs = rLayout.GetOrientation() ? 1 : nMaxGlyphs; // #i97991# temporary workaround for #i87686# 7461 std::vector< PDFGlyph > aGlyphs; 7462 aGlyphs.reserve( nTmpMaxGlyphs ); 7463 // first get all the glyphs and register them; coordinates still in Pixel 7464 Point aGNGlyphPos; 7465 while( (nGlyphs = rLayout.GetNextGlyphs( nTmpMaxGlyphs, pGlyphs, aGNGlyphPos, nIndex, nAdvanceWidths, pCharPosAry )) != 0 ) 7466 { 7467 aUnicodes.clear(); 7468 for( int i = 0; i < nGlyphs; i++ ) 7469 { 7470 pFallbackFonts[i] = rLayout.GetFallbackFontData( pGlyphs[i] ); 7471 7472 // default case: 1 glyph is one unicode 7473 pUnicodesPerGlyph[i] = 1; 7474 if( (pGlyphs[i] & GF_ISCHAR) ) 7475 { 7476 aUnicodes.push_back( static_cast<sal_Ucs>(pGlyphs[i] & GF_IDXMASK) ); 7477 } 7478 else if( pCharPosAry[i] >= nMinCharPos && pCharPosAry[i] <= nMaxCharPos ) 7479 { 7480 int nChars = 1; 7481 aUnicodes.push_back( rText.GetChar( sal::static_int_cast<xub_StrLen>(pCharPosAry[i]) ) ); 7482 pUnicodesPerGlyph[i] = 1; 7483 // try to handle ligatures and such 7484 if( i < nGlyphs-1 ) 7485 { 7486 nChars = pCharPosAry[i+1] - pCharPosAry[i]; 7487 // #i115618# fix for simple RTL+CTL cases 7488 // TODO: sanitize for RTL ligatures, more complex CTL, etc. 7489 if( nChars < 0 ) 7490 nChars = -nChars; 7491 else if( nChars == 0 ) 7492 nChars = 1; 7493 pUnicodesPerGlyph[i] = nChars; 7494 for( int n = 1; n < nChars; n++ ) 7495 aUnicodes.push_back( rText.GetChar( sal::static_int_cast<xub_StrLen>(pCharPosAry[i]+n) ) ); 7496 } 7497 // #i36691# hack that is needed because currently the pGlyphs[] 7498 // argument is ignored for embeddable fonts and so the layout 7499 // engine's glyph work is ignored (i.e. char mirroring) 7500 // TODO: a real solution would be to map the layout engine's 7501 // glyphid (i.e. FreeType's synthetic glyphid for a Type1 font) 7502 // back to unicode and then to embeddable font's encoding 7503 if( getReferenceDevice()->GetLayoutMode() & TEXT_LAYOUT_BIDI_RTL ) 7504 { 7505 size_t nI = aUnicodes.size()-1; 7506 for( int n = 0; n < nChars; n++, nI-- ) 7507 aUnicodes[nI] = static_cast<sal_Ucs>(GetMirroredChar(aUnicodes[nI])); 7508 } 7509 } 7510 else 7511 aUnicodes.push_back( 0 ); 7512 // note: in case of ctl one character may result 7513 // in multiple glyphs. The current SalLayout 7514 // implementations set -1 then to indicate that no direct 7515 // mapping is possible 7516 } 7517 7518 registerGlyphs( nGlyphs, pGlyphs, pGlyphWidths, &aUnicodes[0], pUnicodesPerGlyph, pMappedGlyphs, pMappedFontObjects, pFallbackFonts ); 7519 7520 for( int i = 0; i < nGlyphs; i++ ) 7521 { 7522 aGlyphs.push_back( PDFGlyph( aGNGlyphPos, 7523 pGlyphWidths[i], 7524 pGlyphs[i], 7525 pMappedFontObjects[i], 7526 pMappedGlyphs[i] ) ); 7527 if( bVertical ) 7528 aGNGlyphPos.Y() += nAdvanceWidths[i]/rLayout.GetUnitsPerPixel(); 7529 else 7530 aGNGlyphPos.X() += nAdvanceWidths[i]/rLayout.GetUnitsPerPixel(); 7531 } 7532 } 7533 7534 Point aAlignOffset; 7535 if ( eAlign == ALIGN_BOTTOM ) 7536 aAlignOffset.Y() -= aRefDevFontMetric.GetDescent(); 7537 else if ( eAlign == ALIGN_TOP ) 7538 aAlignOffset.Y() += aRefDevFontMetric.GetAscent(); 7539 if( aAlignOffset.X() || aAlignOffset.Y() ) 7540 aAlignOffset = aRotScale.transform( aAlignOffset ); 7541 7542 /* #159153# do not emit an empty glyph vector; this can happen if e.g. the original 7543 string contained only on of the UTF16 BOMs 7544 */ 7545 if( ! aGlyphs.empty() ) 7546 { 7547 if( bVertical ) 7548 drawVerticalGlyphs( aGlyphs, aLine, aAlignOffset, aRotScale, fAngle, fXScale, fSkew, nFontHeight ); 7549 else 7550 drawHorizontalGlyphs( aGlyphs, aLine, aAlignOffset, fAngle, fXScale, fSkew, nFontHeight, nPixelFontHeight ); 7551 } 7552 7553 // end textobject 7554 aLine.append( "ET\n" ); 7555 if( bPop ) 7556 aLine.append( "Q\n" ); 7557 7558 writeBuffer( aLine.getStr(), aLine.getLength() ); 7559 7560 // draw eventual textlines 7561 FontStrikeout eStrikeout = m_aCurrentPDFState.m_aFont.GetStrikeout(); 7562 FontUnderline eUnderline = m_aCurrentPDFState.m_aFont.GetUnderline(); 7563 FontUnderline eOverline = m_aCurrentPDFState.m_aFont.GetOverline(); 7564 if( bTextLines && 7565 ( 7566 ( eUnderline != UNDERLINE_NONE && eUnderline != UNDERLINE_DONTKNOW ) || 7567 ( eOverline != UNDERLINE_NONE && eOverline != UNDERLINE_DONTKNOW ) || 7568 ( eStrikeout != STRIKEOUT_NONE && eStrikeout != STRIKEOUT_DONTKNOW ) 7569 ) 7570 ) 7571 { 7572 sal_Bool bUnderlineAbove = OutputDevice::ImplIsUnderlineAbove( m_aCurrentPDFState.m_aFont ); 7573 if( m_aCurrentPDFState.m_aFont.IsWordLineMode() ) 7574 { 7575 Point aPos, aStartPt; 7576 sal_Int32 nWidth = 0, nAdvance=0; 7577 for( int nStart = 0;;) 7578 { 7579 sal_GlyphId nGlyphIndex; 7580 if( !rLayout.GetNextGlyphs( 1, &nGlyphIndex, aPos, nStart, &nAdvance ) ) 7581 break; 7582 7583 if( !rLayout.IsSpacingGlyph( nGlyphIndex ) ) 7584 { 7585 if( !nWidth ) 7586 aStartPt = aPos; 7587 7588 nWidth += nAdvance; 7589 } 7590 else if( nWidth > 0 ) 7591 { 7592 drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ), 7593 m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ), 7594 eStrikeout, eUnderline, eOverline, bUnderlineAbove ); 7595 nWidth = 0; 7596 } 7597 } 7598 7599 if( nWidth > 0 ) 7600 { 7601 drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ), 7602 m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ), 7603 eStrikeout, eUnderline, eOverline, bUnderlineAbove ); 7604 } 7605 } 7606 else 7607 { 7608 Point aStartPt = rLayout.GetDrawPosition(); 7609 int nWidth = rLayout.GetTextWidth() / rLayout.GetUnitsPerPixel(); 7610 drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ), 7611 m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ), 7612 eStrikeout, eUnderline, eOverline, bUnderlineAbove ); 7613 } 7614 } 7615 7616 // write eventual emphasis marks 7617 if( m_aCurrentPDFState.m_aFont.GetEmphasisMark() & EMPHASISMARK_STYLE ) 7618 { 7619 PolyPolygon aEmphPoly; 7620 Rectangle aEmphRect1; 7621 Rectangle aEmphRect2; 7622 long nEmphYOff; 7623 long nEmphWidth; 7624 long nEmphHeight; 7625 sal_Bool bEmphPolyLine; 7626 FontEmphasisMark nEmphMark; 7627 7628 push( PUSH_ALL ); 7629 7630 aLine.setLength( 0 ); 7631 aLine.append( "q\n" ); 7632 7633 nEmphMark = m_pReferenceDevice->ImplGetEmphasisMarkStyle( m_aCurrentPDFState.m_aFont ); 7634 if ( nEmphMark & EMPHASISMARK_POS_BELOW ) 7635 nEmphHeight = m_pReferenceDevice->mnEmphasisDescent; 7636 else 7637 nEmphHeight = m_pReferenceDevice->mnEmphasisAscent; 7638 m_pReferenceDevice->ImplGetEmphasisMark( aEmphPoly, 7639 bEmphPolyLine, 7640 aEmphRect1, 7641 aEmphRect2, 7642 nEmphYOff, 7643 nEmphWidth, 7644 nEmphMark, 7645 m_pReferenceDevice->ImplDevicePixelToLogicWidth(nEmphHeight), 7646 m_pReferenceDevice->mpFontEntry->mnOrientation ); 7647 if ( bEmphPolyLine ) 7648 { 7649 setLineColor( m_aCurrentPDFState.m_aFont.GetColor() ); 7650 setFillColor( Color( COL_TRANSPARENT ) ); 7651 } 7652 else 7653 { 7654 setFillColor( m_aCurrentPDFState.m_aFont.GetColor() ); 7655 setLineColor( Color( COL_TRANSPARENT ) ); 7656 } 7657 writeBuffer( aLine.getStr(), aLine.getLength() ); 7658 7659 Point aOffset = Point(0,0); 7660 7661 if ( nEmphMark & EMPHASISMARK_POS_BELOW ) 7662 aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnDescent + nEmphYOff; 7663 else 7664 aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnAscent + nEmphYOff; 7665 7666 long nEmphWidth2 = nEmphWidth / 2; 7667 long nEmphHeight2 = nEmphHeight / 2; 7668 aOffset += Point( nEmphWidth2, nEmphHeight2 ); 7669 7670 if ( eAlign == ALIGN_BOTTOM ) 7671 aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnDescent; 7672 else if ( eAlign == ALIGN_TOP ) 7673 aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnAscent; 7674 7675 for( int nStart = 0;;) 7676 { 7677 Point aPos; 7678 sal_GlyphId nGlyphIndex; 7679 sal_Int32 nAdvance; 7680 if( !rLayout.GetNextGlyphs( 1, &nGlyphIndex, aPos, nStart, &nAdvance ) ) 7681 break; 7682 7683 if( !rLayout.IsSpacingGlyph( nGlyphIndex ) ) 7684 { 7685 Point aAdjOffset = aOffset; 7686 aAdjOffset.X() += (nAdvance - nEmphWidth) / 2; 7687 aAdjOffset = aRotScale.transform( aAdjOffset ); 7688 7689 aAdjOffset -= Point( nEmphWidth2, nEmphHeight2 ); 7690 7691 aPos += aAdjOffset; 7692 aPos = m_pReferenceDevice->PixelToLogic( aPos ); 7693 drawEmphasisMark( aPos.X(), aPos.Y(), 7694 aEmphPoly, bEmphPolyLine, 7695 aEmphRect1, aEmphRect2 ); 7696 } 7697 } 7698 7699 writeBuffer( "Q\n", 2 ); 7700 pop(); 7701 } 7702 7703 } 7704 7705 void PDFWriterImpl::drawEmphasisMark( long nX, long nY, 7706 const PolyPolygon& rPolyPoly, sal_Bool bPolyLine, 7707 const Rectangle& rRect1, const Rectangle& rRect2 ) 7708 { 7709 // TODO: pass nWidth as width of this mark 7710 // long nWidth = 0; 7711 7712 if ( rPolyPoly.Count() ) 7713 { 7714 if ( bPolyLine ) 7715 { 7716 Polygon aPoly = rPolyPoly.GetObject( 0 ); 7717 aPoly.Move( nX, nY ); 7718 drawPolyLine( aPoly ); 7719 } 7720 else 7721 { 7722 PolyPolygon aPolyPoly = rPolyPoly; 7723 aPolyPoly.Move( nX, nY ); 7724 drawPolyPolygon( aPolyPoly ); 7725 } 7726 } 7727 7728 if ( !rRect1.IsEmpty() ) 7729 { 7730 Rectangle aRect( Point( nX+rRect1.Left(), 7731 nY+rRect1.Top() ), rRect1.GetSize() ); 7732 drawRectangle( aRect ); 7733 } 7734 7735 if ( !rRect2.IsEmpty() ) 7736 { 7737 Rectangle aRect( Point( nX+rRect2.Left(), 7738 nY+rRect2.Top() ), rRect2.GetSize() ); 7739 7740 drawRectangle( aRect ); 7741 } 7742 } 7743 7744 void PDFWriterImpl::drawText( const Point& rPos, const String& rText, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines ) 7745 { 7746 MARK( "drawText" ); 7747 7748 updateGraphicsState(); 7749 7750 // get a layout from the OuputDevice's SalGraphics 7751 // this also enforces font substitution and sets the font on SalGraphics 7752 SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos ); 7753 if( pLayout ) 7754 { 7755 drawLayout( *pLayout, rText, bTextLines ); 7756 pLayout->Release(); 7757 } 7758 } 7759 7760 void PDFWriterImpl::drawTextArray( const Point& rPos, const String& rText, const sal_Int32* pDXArray, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines ) 7761 { 7762 MARK( "drawText with array" ); 7763 7764 updateGraphicsState(); 7765 7766 // get a layout from the OuputDevice's SalGraphics 7767 // this also enforces font substitution and sets the font on SalGraphics 7768 SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, 0, pDXArray ); 7769 if( pLayout ) 7770 { 7771 drawLayout( *pLayout, rText, bTextLines ); 7772 pLayout->Release(); 7773 } 7774 } 7775 7776 void PDFWriterImpl::drawStretchText( const Point& rPos, sal_uLong nWidth, const String& rText, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines ) 7777 { 7778 MARK( "drawStretchText" ); 7779 7780 updateGraphicsState(); 7781 7782 // get a layout from the OuputDevice's SalGraphics 7783 // this also enforces font substitution and sets the font on SalGraphics 7784 SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, nWidth ); 7785 if( pLayout ) 7786 { 7787 drawLayout( *pLayout, rText, bTextLines ); 7788 pLayout->Release(); 7789 } 7790 } 7791 7792 void PDFWriterImpl::drawText( const Rectangle& rRect, const String& rOrigStr, sal_uInt16 nStyle, bool bTextLines ) 7793 { 7794 long nWidth = rRect.GetWidth(); 7795 long nHeight = rRect.GetHeight(); 7796 7797 if ( nWidth <= 0 || nHeight <= 0 ) 7798 return; 7799 7800 MARK( "drawText with rectangle" ); 7801 7802 updateGraphicsState(); 7803 7804 // clip with rectangle 7805 OStringBuffer aLine; 7806 aLine.append( "q " ); 7807 m_aPages.back().appendRect( rRect, aLine ); 7808 aLine.append( " W* n\n" ); 7809 writeBuffer( aLine.getStr(), aLine.getLength() ); 7810 7811 // if disabled text is needed, put in here 7812 7813 Point aPos = rRect.TopLeft(); 7814 7815 long nTextHeight = m_pReferenceDevice->GetTextHeight(); 7816 xub_StrLen nMnemonicPos = STRING_NOTFOUND; 7817 7818 String aStr = rOrigStr; 7819 if ( nStyle & TEXT_DRAW_MNEMONIC ) 7820 aStr = m_pReferenceDevice->GetNonMnemonicString( aStr, nMnemonicPos ); 7821 7822 // multiline text 7823 if ( nStyle & TEXT_DRAW_MULTILINE ) 7824 { 7825 XubString aLastLine; 7826 ImplMultiTextLineInfo aMultiLineInfo; 7827 ImplTextLineInfo* pLineInfo; 7828 long nMaxTextWidth; 7829 xub_StrLen i; 7830 xub_StrLen nLines; 7831 xub_StrLen nFormatLines; 7832 7833 if ( nTextHeight ) 7834 { 7835 ::vcl::DefaultTextLayout aLayout( *m_pReferenceDevice ); 7836 nMaxTextWidth = OutputDevice::ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle, aLayout ); 7837 nLines = (xub_StrLen)(nHeight/nTextHeight); 7838 nFormatLines = aMultiLineInfo.Count(); 7839 if ( !nLines ) 7840 nLines = 1; 7841 if ( nFormatLines > nLines ) 7842 { 7843 if ( nStyle & TEXT_DRAW_ENDELLIPSIS ) 7844 { 7845 // handle last line 7846 nFormatLines = nLines-1; 7847 7848 pLineInfo = aMultiLineInfo.GetLine( nFormatLines ); 7849 aLastLine = aStr.Copy( pLineInfo->GetIndex() ); 7850 aLastLine.ConvertLineEnd( LINEEND_LF ); 7851 // replace line feed by space 7852 xub_StrLen nLastLineLen = aLastLine.Len(); 7853 for ( i = 0; i < nLastLineLen; i++ ) 7854 { 7855 if ( aLastLine.GetChar( i ) == _LF ) 7856 aLastLine.SetChar( i, ' ' ); 7857 } 7858 aLastLine = m_pReferenceDevice->GetEllipsisString( aLastLine, nWidth, nStyle ); 7859 nStyle &= ~(TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM); 7860 nStyle |= TEXT_DRAW_TOP; 7861 } 7862 } 7863 7864 // vertical alignment 7865 if ( nStyle & TEXT_DRAW_BOTTOM ) 7866 aPos.Y() += nHeight-(nFormatLines*nTextHeight); 7867 else if ( nStyle & TEXT_DRAW_VCENTER ) 7868 aPos.Y() += (nHeight-(nFormatLines*nTextHeight))/2; 7869 7870 // draw all lines excluding the last 7871 for ( i = 0; i < nFormatLines; i++ ) 7872 { 7873 pLineInfo = aMultiLineInfo.GetLine( i ); 7874 if ( nStyle & TEXT_DRAW_RIGHT ) 7875 aPos.X() += nWidth-pLineInfo->GetWidth(); 7876 else if ( nStyle & TEXT_DRAW_CENTER ) 7877 aPos.X() += (nWidth-pLineInfo->GetWidth())/2; 7878 xub_StrLen nIndex = pLineInfo->GetIndex(); 7879 xub_StrLen nLineLen = pLineInfo->GetLen(); 7880 drawText( aPos, aStr, nIndex, nLineLen, bTextLines ); 7881 // mnemonics should not appear in documents, 7882 // if the need arises, put them in here 7883 aPos.Y() += nTextHeight; 7884 aPos.X() = rRect.Left(); 7885 } 7886 7887 7888 // output last line left adjusted since it was shortened 7889 if ( aLastLine.Len() ) 7890 drawText( aPos, aLastLine, 0, STRING_LEN, bTextLines ); 7891 } 7892 } 7893 else 7894 { 7895 long nTextWidth = m_pReferenceDevice->GetTextWidth( aStr ); 7896 7897 // Evt. Text kuerzen 7898 if ( nTextWidth > nWidth ) 7899 { 7900 if ( nStyle & (TEXT_DRAW_ENDELLIPSIS | TEXT_DRAW_PATHELLIPSIS | TEXT_DRAW_NEWSELLIPSIS) ) 7901 { 7902 aStr = m_pReferenceDevice->GetEllipsisString( aStr, nWidth, nStyle ); 7903 nStyle &= ~(TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT); 7904 nStyle |= TEXT_DRAW_LEFT; 7905 nTextWidth = m_pReferenceDevice->GetTextWidth( aStr ); 7906 } 7907 } 7908 7909 // vertical alignment 7910 if ( nStyle & TEXT_DRAW_RIGHT ) 7911 aPos.X() += nWidth-nTextWidth; 7912 else if ( nStyle & TEXT_DRAW_CENTER ) 7913 aPos.X() += (nWidth-nTextWidth)/2; 7914 7915 if ( nStyle & TEXT_DRAW_BOTTOM ) 7916 aPos.Y() += nHeight-nTextHeight; 7917 else if ( nStyle & TEXT_DRAW_VCENTER ) 7918 aPos.Y() += (nHeight-nTextHeight)/2; 7919 7920 // mnemonics should be inserted here if the need arises 7921 7922 // draw the actual text 7923 drawText( aPos, aStr, 0, STRING_LEN, bTextLines ); 7924 } 7925 7926 // reset clip region to original value 7927 aLine.setLength( 0 ); 7928 aLine.append( "Q\n" ); 7929 writeBuffer( aLine.getStr(), aLine.getLength() ); 7930 } 7931 7932 void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop ) 7933 { 7934 MARK( "drawLine" ); 7935 7936 updateGraphicsState(); 7937 7938 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) 7939 return; 7940 7941 OStringBuffer aLine; 7942 m_aPages.back().appendPoint( rStart, aLine ); 7943 aLine.append( " m " ); 7944 m_aPages.back().appendPoint( rStop, aLine ); 7945 aLine.append( " l S\n" ); 7946 7947 writeBuffer( aLine.getStr(), aLine.getLength() ); 7948 } 7949 7950 void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop, const LineInfo& rInfo ) 7951 { 7952 MARK( "drawLine with LineInfo" ); 7953 updateGraphicsState(); 7954 7955 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) 7956 return; 7957 7958 if( rInfo.GetStyle() == LINE_SOLID && rInfo.GetWidth() < 2 ) 7959 { 7960 drawLine( rStart, rStop ); 7961 return; 7962 } 7963 7964 OStringBuffer aLine; 7965 7966 aLine.append( "q " ); 7967 if( m_aPages.back().appendLineInfo( rInfo, aLine ) ) 7968 { 7969 m_aPages.back().appendPoint( rStart, aLine ); 7970 aLine.append( " m " ); 7971 m_aPages.back().appendPoint( rStop, aLine ); 7972 aLine.append( " l S Q\n" ); 7973 7974 writeBuffer( aLine.getStr(), aLine.getLength() ); 7975 } 7976 else 7977 { 7978 PDFWriter::ExtLineInfo aInfo; 7979 convertLineInfoToExtLineInfo( rInfo, aInfo ); 7980 Point aPolyPoints[2] = { rStart, rStop }; 7981 Polygon aPoly( 2, aPolyPoints ); 7982 drawPolyLine( aPoly, aInfo ); 7983 } 7984 } 7985 7986 void PDFWriterImpl::drawWaveLine( const Point& rStart, const Point& rStop, sal_Int32 nDelta, sal_Int32 nLineWidth ) 7987 { 7988 Point aDiff( rStop-rStart ); 7989 double fLen = sqrt( (double)(aDiff.X()*aDiff.X() + aDiff.Y()*aDiff.Y()) ); 7990 if( fLen < 1.0 ) 7991 return; 7992 7993 MARK( "drawWaveLine" ); 7994 updateGraphicsState(); 7995 7996 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) 7997 return; 7998 7999 OStringBuffer aLine( 512 ); 8000 aLine.append( "q " ); 8001 m_aPages.back().appendMappedLength( nLineWidth, aLine, true ); 8002 aLine.append( " w " ); 8003 8004 appendDouble( (double)aDiff.X()/fLen, aLine ); 8005 aLine.append( ' ' ); 8006 appendDouble( -(double)aDiff.Y()/fLen, aLine ); 8007 aLine.append( ' ' ); 8008 appendDouble( (double)aDiff.Y()/fLen, aLine ); 8009 aLine.append( ' ' ); 8010 appendDouble( (double)aDiff.X()/fLen, aLine ); 8011 aLine.append( ' ' ); 8012 m_aPages.back().appendPoint( rStart, aLine ); 8013 aLine.append( " cm " ); 8014 m_aPages.back().appendWaveLine( (sal_Int32)fLen, 0, nDelta, aLine ); 8015 aLine.append( "Q\n" ); 8016 writeBuffer( aLine.getStr(), aLine.getLength() ); 8017 } 8018 8019 #define WCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicWidth( x ) 8020 #define HCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicHeight( x ) 8021 8022 void PDFWriterImpl::drawWaveTextLine( OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove ) 8023 { 8024 // note: units in pFontEntry are ref device pixel 8025 ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry; 8026 long nLineHeight = 0; 8027 long nLinePos = 0; 8028 8029 appendStrokingColor( aColor, aLine ); 8030 aLine.append( "\n" ); 8031 8032 if ( bIsAbove ) 8033 { 8034 if ( !pFontEntry->maMetric.mnAboveWUnderlineSize ) 8035 m_pReferenceDevice->ImplInitAboveTextLineSize(); 8036 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveWUnderlineSize ); 8037 nLinePos = HCONV( pFontEntry->maMetric.mnAboveWUnderlineOffset ); 8038 } 8039 else 8040 { 8041 if ( !pFontEntry->maMetric.mnWUnderlineSize ) 8042 m_pReferenceDevice->ImplInitTextLineSize(); 8043 nLineHeight = HCONV( pFontEntry->maMetric.mnWUnderlineSize ); 8044 nLinePos = HCONV( pFontEntry->maMetric.mnWUnderlineOffset ); 8045 } 8046 if ( (eTextLine == UNDERLINE_SMALLWAVE) && (nLineHeight > 3) ) 8047 nLineHeight = 3; 8048 8049 long nLineWidth = getReferenceDevice()->mnDPIX/450; 8050 if ( ! nLineWidth ) 8051 nLineWidth = 1; 8052 8053 if ( eTextLine == UNDERLINE_BOLDWAVE ) 8054 nLineWidth = 3*nLineWidth; 8055 8056 m_aPages.back().appendMappedLength( (sal_Int32)nLineWidth, aLine ); 8057 aLine.append( " w " ); 8058 8059 if ( eTextLine == UNDERLINE_DOUBLEWAVE ) 8060 { 8061 long nOrgLineHeight = nLineHeight; 8062 nLineHeight /= 3; 8063 if ( nLineHeight < 2 ) 8064 { 8065 if ( nOrgLineHeight > 1 ) 8066 nLineHeight = 2; 8067 else 8068 nLineHeight = 1; 8069 } 8070 long nLineDY = nOrgLineHeight-(nLineHeight*2); 8071 if ( nLineDY < nLineWidth ) 8072 nLineDY = nLineWidth; 8073 long nLineDY2 = nLineDY/2; 8074 if ( !nLineDY2 ) 8075 nLineDY2 = 1; 8076 8077 nLinePos -= nLineWidth-nLineDY2; 8078 8079 m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine ); 8080 8081 nLinePos += nLineWidth+nLineDY; 8082 m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine ); 8083 } 8084 else 8085 { 8086 if ( eTextLine != UNDERLINE_BOLDWAVE ) 8087 nLinePos -= nLineWidth/2; 8088 m_aPages.back().appendWaveLine( nWidth, -nLinePos, nLineHeight, aLine ); 8089 } 8090 } 8091 8092 void PDFWriterImpl::drawStraightTextLine( OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove ) 8093 { 8094 // note: units in pFontEntry are ref device pixel 8095 ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry; 8096 long nLineHeight = 0; 8097 long nLinePos = 0; 8098 long nLinePos2 = 0; 8099 8100 if ( eTextLine > UNDERLINE_BOLDWAVE ) 8101 eTextLine = UNDERLINE_SINGLE; 8102 8103 switch ( eTextLine ) 8104 { 8105 case UNDERLINE_SINGLE: 8106 case UNDERLINE_DOTTED: 8107 case UNDERLINE_DASH: 8108 case UNDERLINE_LONGDASH: 8109 case UNDERLINE_DASHDOT: 8110 case UNDERLINE_DASHDOTDOT: 8111 if ( bIsAbove ) 8112 { 8113 if ( !pFontEntry->maMetric.mnAboveUnderlineSize ) 8114 m_pReferenceDevice->ImplInitAboveTextLineSize(); 8115 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveUnderlineSize ); 8116 nLinePos = HCONV( pFontEntry->maMetric.mnAboveUnderlineOffset ); 8117 } 8118 else 8119 { 8120 if ( !pFontEntry->maMetric.mnUnderlineSize ) 8121 m_pReferenceDevice->ImplInitTextLineSize(); 8122 nLineHeight = HCONV( pFontEntry->maMetric.mnUnderlineSize ); 8123 nLinePos = HCONV( pFontEntry->maMetric.mnUnderlineOffset ); 8124 } 8125 break; 8126 case UNDERLINE_BOLD: 8127 case UNDERLINE_BOLDDOTTED: 8128 case UNDERLINE_BOLDDASH: 8129 case UNDERLINE_BOLDLONGDASH: 8130 case UNDERLINE_BOLDDASHDOT: 8131 case UNDERLINE_BOLDDASHDOTDOT: 8132 if ( bIsAbove ) 8133 { 8134 if ( !pFontEntry->maMetric.mnAboveBUnderlineSize ) 8135 m_pReferenceDevice->ImplInitAboveTextLineSize(); 8136 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveBUnderlineSize ); 8137 nLinePos = HCONV( pFontEntry->maMetric.mnAboveBUnderlineOffset ); 8138 } 8139 else 8140 { 8141 if ( !pFontEntry->maMetric.mnBUnderlineSize ) 8142 m_pReferenceDevice->ImplInitTextLineSize(); 8143 nLineHeight = HCONV( pFontEntry->maMetric.mnBUnderlineSize ); 8144 nLinePos = HCONV( pFontEntry->maMetric.mnBUnderlineOffset ); 8145 nLinePos += nLineHeight/2; 8146 } 8147 break; 8148 case UNDERLINE_DOUBLE: 8149 if ( bIsAbove ) 8150 { 8151 if ( !pFontEntry->maMetric.mnAboveDUnderlineSize ) 8152 m_pReferenceDevice->ImplInitAboveTextLineSize(); 8153 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveDUnderlineSize ); 8154 nLinePos = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset1 ); 8155 nLinePos2 = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset2 ); 8156 } 8157 else 8158 { 8159 if ( !pFontEntry->maMetric.mnDUnderlineSize ) 8160 m_pReferenceDevice->ImplInitTextLineSize(); 8161 nLineHeight = HCONV( pFontEntry->maMetric.mnDUnderlineSize ); 8162 nLinePos = HCONV( pFontEntry->maMetric.mnDUnderlineOffset1 ); 8163 nLinePos2 = HCONV( pFontEntry->maMetric.mnDUnderlineOffset2 ); 8164 } 8165 default: 8166 break; 8167 } 8168 8169 if ( nLineHeight ) 8170 { 8171 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, true ); 8172 aLine.append( " w " ); 8173 appendStrokingColor( aColor, aLine ); 8174 aLine.append( "\n" ); 8175 8176 switch ( eTextLine ) 8177 { 8178 case UNDERLINE_DOTTED: 8179 case UNDERLINE_BOLDDOTTED: 8180 aLine.append( "[ " ); 8181 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false ); 8182 aLine.append( " ] 0 d\n" ); 8183 break; 8184 case UNDERLINE_DASH: 8185 case UNDERLINE_LONGDASH: 8186 case UNDERLINE_BOLDDASH: 8187 case UNDERLINE_BOLDLONGDASH: 8188 { 8189 sal_Int32 nDashLength = 4*nLineHeight; 8190 sal_Int32 nVoidLength = 2*nLineHeight; 8191 if ( ( eTextLine == UNDERLINE_LONGDASH ) || ( eTextLine == UNDERLINE_BOLDLONGDASH ) ) 8192 nDashLength = 8*nLineHeight; 8193 8194 aLine.append( "[ " ); 8195 m_aPages.back().appendMappedLength( nDashLength, aLine, false ); 8196 aLine.append( ' ' ); 8197 m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); 8198 aLine.append( " ] 0 d\n" ); 8199 } 8200 break; 8201 case UNDERLINE_DASHDOT: 8202 case UNDERLINE_BOLDDASHDOT: 8203 { 8204 sal_Int32 nDashLength = 4*nLineHeight; 8205 sal_Int32 nVoidLength = 2*nLineHeight; 8206 aLine.append( "[ " ); 8207 m_aPages.back().appendMappedLength( nDashLength, aLine, false ); 8208 aLine.append( ' ' ); 8209 m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); 8210 aLine.append( ' ' ); 8211 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false ); 8212 aLine.append( ' ' ); 8213 m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); 8214 aLine.append( " ] 0 d\n" ); 8215 } 8216 break; 8217 case UNDERLINE_DASHDOTDOT: 8218 case UNDERLINE_BOLDDASHDOTDOT: 8219 { 8220 sal_Int32 nDashLength = 4*nLineHeight; 8221 sal_Int32 nVoidLength = 2*nLineHeight; 8222 aLine.append( "[ " ); 8223 m_aPages.back().appendMappedLength( nDashLength, aLine, false ); 8224 aLine.append( ' ' ); 8225 m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); 8226 aLine.append( ' ' ); 8227 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false ); 8228 aLine.append( ' ' ); 8229 m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); 8230 aLine.append( ' ' ); 8231 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false ); 8232 aLine.append( ' ' ); 8233 m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); 8234 aLine.append( " ] 0 d\n" ); 8235 } 8236 break; 8237 default: 8238 break; 8239 } 8240 8241 aLine.append( "0 " ); 8242 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true ); 8243 aLine.append( " m " ); 8244 m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false ); 8245 aLine.append( ' ' ); 8246 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true ); 8247 aLine.append( " l S\n" ); 8248 if ( eTextLine == UNDERLINE_DOUBLE ) 8249 { 8250 aLine.append( "0 " ); 8251 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true ); 8252 aLine.append( " m " ); 8253 m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false ); 8254 aLine.append( ' ' ); 8255 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true ); 8256 aLine.append( " l S\n" ); 8257 } 8258 } 8259 } 8260 8261 void PDFWriterImpl::drawStrikeoutLine( OStringBuffer& aLine, long nWidth, FontStrikeout eStrikeout, Color aColor ) 8262 { 8263 // note: units in pFontEntry are ref device pixel 8264 ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry; 8265 long nLineHeight = 0; 8266 long nLinePos = 0; 8267 long nLinePos2 = 0; 8268 8269 if ( eStrikeout > STRIKEOUT_X ) 8270 eStrikeout = STRIKEOUT_SINGLE; 8271 8272 switch ( eStrikeout ) 8273 { 8274 case STRIKEOUT_SINGLE: 8275 if ( !pFontEntry->maMetric.mnStrikeoutSize ) 8276 m_pReferenceDevice->ImplInitTextLineSize(); 8277 nLineHeight = HCONV( pFontEntry->maMetric.mnStrikeoutSize ); 8278 nLinePos = HCONV( pFontEntry->maMetric.mnStrikeoutOffset ); 8279 break; 8280 case STRIKEOUT_BOLD: 8281 if ( !pFontEntry->maMetric.mnBStrikeoutSize ) 8282 m_pReferenceDevice->ImplInitTextLineSize(); 8283 nLineHeight = HCONV( pFontEntry->maMetric.mnBStrikeoutSize ); 8284 nLinePos = HCONV( pFontEntry->maMetric.mnBStrikeoutOffset ); 8285 break; 8286 case STRIKEOUT_DOUBLE: 8287 if ( !pFontEntry->maMetric.mnDStrikeoutSize ) 8288 m_pReferenceDevice->ImplInitTextLineSize(); 8289 nLineHeight = HCONV( pFontEntry->maMetric.mnDStrikeoutSize ); 8290 nLinePos = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset1 ); 8291 nLinePos2 = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset2 ); 8292 break; 8293 default: 8294 break; 8295 } 8296 8297 if ( nLineHeight ) 8298 { 8299 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, true ); 8300 aLine.append( " w " ); 8301 appendStrokingColor( aColor, aLine ); 8302 aLine.append( "\n" ); 8303 8304 aLine.append( "0 " ); 8305 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true ); 8306 aLine.append( " m " ); 8307 m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, true ); 8308 aLine.append( ' ' ); 8309 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true ); 8310 aLine.append( " l S\n" ); 8311 8312 if ( eStrikeout == STRIKEOUT_DOUBLE ) 8313 { 8314 aLine.append( "0 " ); 8315 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true ); 8316 aLine.append( " m " ); 8317 m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, true ); 8318 aLine.append( ' ' ); 8319 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true ); 8320 aLine.append( " l S\n" ); 8321 } 8322 } 8323 } 8324 8325 void PDFWriterImpl::drawStrikeoutChar( const Point& rPos, long nWidth, FontStrikeout eStrikeout ) 8326 { 8327 String aStrikeoutChar = String::CreateFromAscii( eStrikeout == STRIKEOUT_SLASH ? "/" : "X" ); 8328 String aStrikeout = aStrikeoutChar; 8329 while( m_pReferenceDevice->GetTextWidth( aStrikeout ) < nWidth ) 8330 aStrikeout.Append( aStrikeout ); 8331 8332 // do not get broader than nWidth modulo 1 character 8333 while( m_pReferenceDevice->GetTextWidth( aStrikeout ) >= nWidth ) 8334 aStrikeout.Erase( 0, 1 ); 8335 aStrikeout.Append( aStrikeoutChar ); 8336 sal_Bool bShadow = m_aCurrentPDFState.m_aFont.IsShadow(); 8337 if ( bShadow ) 8338 { 8339 Font aFont = m_aCurrentPDFState.m_aFont; 8340 aFont.SetShadow( sal_False ); 8341 setFont( aFont ); 8342 updateGraphicsState(); 8343 } 8344 8345 // strikeout string is left aligned non-CTL text 8346 sal_uLong nOrigTLM = m_pReferenceDevice->GetLayoutMode(); 8347 m_pReferenceDevice->SetLayoutMode( TEXT_LAYOUT_BIDI_STRONG|TEXT_LAYOUT_COMPLEX_DISABLED ); 8348 drawText( rPos, aStrikeout, 0, aStrikeout.Len(), false ); 8349 m_pReferenceDevice->SetLayoutMode( nOrigTLM ); 8350 8351 if ( bShadow ) 8352 { 8353 Font aFont = m_aCurrentPDFState.m_aFont; 8354 aFont.SetShadow( sal_True ); 8355 setFont( aFont ); 8356 updateGraphicsState(); 8357 } 8358 } 8359 8360 void PDFWriterImpl::drawTextLine( const Point& rPos, long nWidth, FontStrikeout eStrikeout, FontUnderline eUnderline, FontUnderline eOverline, bool bUnderlineAbove ) 8361 { 8362 if ( !nWidth || 8363 ( ((eStrikeout == STRIKEOUT_NONE)||(eStrikeout == STRIKEOUT_DONTKNOW)) && 8364 ((eUnderline == UNDERLINE_NONE)||(eUnderline == UNDERLINE_DONTKNOW)) && 8365 ((eOverline == UNDERLINE_NONE)||(eOverline == UNDERLINE_DONTKNOW)) ) ) 8366 return; 8367 8368 MARK( "drawTextLine" ); 8369 updateGraphicsState(); 8370 8371 // note: units in pFontEntry are ref device pixel 8372 ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry; 8373 Color aUnderlineColor = m_aCurrentPDFState.m_aTextLineColor; 8374 Color aOverlineColor = m_aCurrentPDFState.m_aOverlineColor; 8375 Color aStrikeoutColor = m_aCurrentPDFState.m_aFont.GetColor(); 8376 bool bStrikeoutDone = false; 8377 bool bUnderlineDone = false; 8378 bool bOverlineDone = false; 8379 8380 if ( (eStrikeout == STRIKEOUT_SLASH) || (eStrikeout == STRIKEOUT_X) ) 8381 { 8382 drawStrikeoutChar( rPos, nWidth, eStrikeout ); 8383 bStrikeoutDone = true; 8384 } 8385 8386 Point aPos( rPos ); 8387 TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign(); 8388 if( eAlign == ALIGN_TOP ) 8389 aPos.Y() += HCONV( pFontEntry->maMetric.mnAscent ); 8390 else if( eAlign == ALIGN_BOTTOM ) 8391 aPos.Y() -= HCONV( pFontEntry->maMetric.mnDescent ); 8392 8393 OStringBuffer aLine( 512 ); 8394 // save GS 8395 aLine.append( "q " ); 8396 8397 // rotate and translate matrix 8398 double fAngle = (double)m_aCurrentPDFState.m_aFont.GetOrientation() * M_PI / 1800.0; 8399 Matrix3 aMat; 8400 aMat.rotate( fAngle ); 8401 aMat.translate( aPos.X(), aPos.Y() ); 8402 aMat.append( m_aPages.back(), aLine ); 8403 aLine.append( " cm\n" ); 8404 8405 if ( aUnderlineColor.GetTransparency() != 0 ) 8406 aUnderlineColor = aStrikeoutColor; 8407 8408 if ( (eUnderline == UNDERLINE_SMALLWAVE) || 8409 (eUnderline == UNDERLINE_WAVE) || 8410 (eUnderline == UNDERLINE_DOUBLEWAVE) || 8411 (eUnderline == UNDERLINE_BOLDWAVE) ) 8412 { 8413 drawWaveTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove ); 8414 bUnderlineDone = true; 8415 } 8416 8417 if ( (eOverline == UNDERLINE_SMALLWAVE) || 8418 (eOverline == UNDERLINE_WAVE) || 8419 (eOverline == UNDERLINE_DOUBLEWAVE) || 8420 (eOverline == UNDERLINE_BOLDWAVE) ) 8421 { 8422 drawWaveTextLine( aLine, nWidth, eOverline, aOverlineColor, true ); 8423 bOverlineDone = true; 8424 } 8425 8426 if ( !bUnderlineDone ) 8427 { 8428 drawStraightTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove ); 8429 } 8430 8431 if ( !bOverlineDone ) 8432 { 8433 drawStraightTextLine( aLine, nWidth, eOverline, aOverlineColor, true ); 8434 } 8435 8436 if ( !bStrikeoutDone ) 8437 { 8438 drawStrikeoutLine( aLine, nWidth, eStrikeout, aStrikeoutColor ); 8439 } 8440 8441 aLine.append( "Q\n" ); 8442 writeBuffer( aLine.getStr(), aLine.getLength() ); 8443 } 8444 8445 void PDFWriterImpl::drawPolygon( const Polygon& rPoly ) 8446 { 8447 MARK( "drawPolygon" ); 8448 8449 updateGraphicsState(); 8450 8451 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 8452 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 8453 return; 8454 8455 int nPoints = rPoly.GetSize(); 8456 OStringBuffer aLine( 20 * nPoints ); 8457 m_aPages.back().appendPolygon( rPoly, aLine ); 8458 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && 8459 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) 8460 aLine.append( "B*\n" ); 8461 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 8462 aLine.append( "S\n" ); 8463 else 8464 aLine.append( "f*\n" ); 8465 8466 writeBuffer( aLine.getStr(), aLine.getLength() ); 8467 } 8468 8469 void PDFWriterImpl::drawPolyPolygon( const PolyPolygon& rPolyPoly ) 8470 { 8471 MARK( "drawPolyPolygon" ); 8472 8473 updateGraphicsState(); 8474 8475 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 8476 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 8477 return; 8478 8479 int nPolygons = rPolyPoly.Count(); 8480 8481 OStringBuffer aLine( 40 * nPolygons ); 8482 m_aPages.back().appendPolyPolygon( rPolyPoly, aLine ); 8483 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && 8484 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) 8485 aLine.append( "B*\n" ); 8486 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 8487 aLine.append( "S\n" ); 8488 else 8489 aLine.append( "f*\n" ); 8490 8491 writeBuffer( aLine.getStr(), aLine.getLength() ); 8492 } 8493 8494 void PDFWriterImpl::drawTransparent( const PolyPolygon& rPolyPoly, sal_uInt32 nTransparentPercent ) 8495 { 8496 DBG_ASSERT( nTransparentPercent <= 100, "invalid alpha value" ); 8497 nTransparentPercent = nTransparentPercent % 100; 8498 8499 MARK( "drawTransparent" ); 8500 8501 updateGraphicsState(); 8502 8503 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 8504 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 8505 return; 8506 8507 if( m_bIsPDF_A1 || m_aContext.Version < PDFWriter::PDF_1_4 ) 8508 { 8509 m_aErrors.insert( m_bIsPDF_A1 ? 8510 PDFWriter::Warning_Transparency_Omitted_PDFA : 8511 PDFWriter::Warning_Transparency_Omitted_PDF13 ); 8512 8513 drawPolyPolygon( rPolyPoly ); 8514 return; 8515 } 8516 8517 // create XObject 8518 m_aTransparentObjects.push_back( TransparencyEmit() ); 8519 // FIXME: polygons with beziers may yield incorrect bound rect 8520 m_aTransparentObjects.back().m_aBoundRect = rPolyPoly.GetBoundRect(); 8521 // convert rectangle to default user space 8522 m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect ); 8523 m_aTransparentObjects.back().m_nObject = createObject(); 8524 m_aTransparentObjects.back().m_nExtGStateObject = createObject(); 8525 m_aTransparentObjects.back().m_fAlpha = (double)(100-nTransparentPercent) / 100.0; 8526 m_aTransparentObjects.back().m_pContentStream = new SvMemoryStream( 256, 256 ); 8527 // create XObject's content stream 8528 OStringBuffer aContent( 256 ); 8529 m_aPages.back().appendPolyPolygon( rPolyPoly, aContent ); 8530 if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) && 8531 m_aCurrentPDFState.m_aFillColor != Color( COL_TRANSPARENT ) ) 8532 aContent.append( " B*\n" ); 8533 else if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) ) 8534 aContent.append( " S\n" ); 8535 else 8536 aContent.append( " f*\n" ); 8537 m_aTransparentObjects.back().m_pContentStream->Write( aContent.getStr(), aContent.getLength() ); 8538 8539 OStringBuffer aObjName( 16 ); 8540 aObjName.append( "Tr" ); 8541 aObjName.append( m_aTransparentObjects.back().m_nObject ); 8542 OString aTrName( aObjName.makeStringAndClear() ); 8543 aObjName.append( "EGS" ); 8544 aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject ); 8545 OString aExtName( aObjName.makeStringAndClear() ); 8546 8547 OStringBuffer aLine( 80 ); 8548 // insert XObject 8549 aLine.append( "q /" ); 8550 aLine.append( aExtName ); 8551 aLine.append( " gs /" ); 8552 aLine.append( aTrName ); 8553 aLine.append( " Do Q\n" ); 8554 writeBuffer( aLine.getStr(), aLine.getLength() ); 8555 8556 pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject ); 8557 pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject ); 8558 } 8559 8560 void PDFWriterImpl::pushResource( ResourceKind eKind, const OString& rResource, sal_Int32 nObject ) 8561 { 8562 if( nObject >= 0 ) 8563 { 8564 switch( eKind ) 8565 { 8566 case ResXObject: 8567 m_aGlobalResourceDict.m_aXObjects[ rResource ] = nObject; 8568 if( ! m_aOutputStreams.empty() ) 8569 m_aOutputStreams.front().m_aResourceDict.m_aXObjects[ rResource ] = nObject; 8570 break; 8571 case ResExtGState: 8572 m_aGlobalResourceDict.m_aExtGStates[ rResource ] = nObject; 8573 if( ! m_aOutputStreams.empty() ) 8574 m_aOutputStreams.front().m_aResourceDict.m_aExtGStates[ rResource ] = nObject; 8575 break; 8576 case ResShading: 8577 m_aGlobalResourceDict.m_aShadings[ rResource ] = nObject; 8578 if( ! m_aOutputStreams.empty() ) 8579 m_aOutputStreams.front().m_aResourceDict.m_aShadings[ rResource ] = nObject; 8580 break; 8581 case ResPattern: 8582 m_aGlobalResourceDict.m_aPatterns[ rResource ] = nObject; 8583 if( ! m_aOutputStreams.empty() ) 8584 m_aOutputStreams.front().m_aResourceDict.m_aPatterns[ rResource ] = nObject; 8585 break; 8586 } 8587 } 8588 } 8589 8590 void PDFWriterImpl::beginRedirect( SvStream* pStream, const Rectangle& rTargetRect ) 8591 { 8592 push( PUSH_ALL ); 8593 8594 // force reemitting clip region 8595 clearClipRegion(); 8596 updateGraphicsState(); 8597 8598 m_aOutputStreams.push_front( StreamRedirect() ); 8599 m_aOutputStreams.front().m_pStream = pStream; 8600 m_aOutputStreams.front().m_aMapMode = m_aMapMode; 8601 8602 if( !rTargetRect.IsEmpty() ) 8603 { 8604 m_aOutputStreams.front().m_aTargetRect = 8605 lcl_convert( m_aGraphicsStack.front().m_aMapMode, 8606 m_aMapMode, 8607 getReferenceDevice(), 8608 rTargetRect ); 8609 Point aDelta = m_aOutputStreams.front().m_aTargetRect.BottomLeft(); 8610 long nPageHeight = pointToPixel(m_aPages[m_nCurrentPage].getHeight()); 8611 aDelta.Y() = -(nPageHeight - m_aOutputStreams.front().m_aTargetRect.Bottom()); 8612 m_aMapMode.SetOrigin( m_aMapMode.GetOrigin() + aDelta ); 8613 } 8614 8615 // setup graphics state for independent object stream 8616 8617 // force reemitting colors 8618 m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT ); 8619 m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT ); 8620 } 8621 8622 Rectangle PDFWriterImpl::getRedirectTargetRect() const 8623 { 8624 return m_aOutputStreams.empty() ? Rectangle() : m_aOutputStreams.front().m_aTargetRect; 8625 } 8626 8627 SvStream* PDFWriterImpl::endRedirect() 8628 { 8629 SvStream* pStream = NULL; 8630 if( ! m_aOutputStreams.empty() ) 8631 { 8632 pStream = m_aOutputStreams.front().m_pStream; 8633 m_aMapMode = m_aOutputStreams.front().m_aMapMode; 8634 m_aOutputStreams.pop_front(); 8635 } 8636 8637 pop(); 8638 // force reemitting colors and clip region 8639 clearClipRegion(); 8640 m_aCurrentPDFState.m_bClipRegion = m_aGraphicsStack.front().m_bClipRegion; 8641 m_aCurrentPDFState.m_aClipRegion = m_aGraphicsStack.front().m_aClipRegion; 8642 m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT ); 8643 m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT ); 8644 8645 updateGraphicsState(); 8646 8647 return pStream; 8648 } 8649 8650 void PDFWriterImpl::beginTransparencyGroup() 8651 { 8652 updateGraphicsState(); 8653 if( m_aContext.Version >= PDFWriter::PDF_1_4 ) 8654 beginRedirect( new SvMemoryStream( 1024, 1024 ), Rectangle() ); 8655 } 8656 8657 void PDFWriterImpl::endTransparencyGroup( const Rectangle& rBoundingBox, sal_uInt32 nTransparentPercent ) 8658 { 8659 DBG_ASSERT( nTransparentPercent <= 100, "invalid alpha value" ); 8660 nTransparentPercent = nTransparentPercent % 100; 8661 8662 if( m_aContext.Version >= PDFWriter::PDF_1_4 ) 8663 { 8664 // create XObject 8665 m_aTransparentObjects.push_back( TransparencyEmit() ); 8666 m_aTransparentObjects.back().m_aBoundRect = rBoundingBox; 8667 // convert rectangle to default user space 8668 m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect ); 8669 m_aTransparentObjects.back().m_nObject = createObject(); 8670 m_aTransparentObjects.back().m_fAlpha = (double)(100-nTransparentPercent) / 100.0; 8671 // get XObject's content stream 8672 m_aTransparentObjects.back().m_pContentStream = static_cast<SvMemoryStream*>(endRedirect()); 8673 m_aTransparentObjects.back().m_nExtGStateObject = createObject(); 8674 8675 OStringBuffer aObjName( 16 ); 8676 aObjName.append( "Tr" ); 8677 aObjName.append( m_aTransparentObjects.back().m_nObject ); 8678 OString aTrName( aObjName.makeStringAndClear() ); 8679 aObjName.append( "EGS" ); 8680 aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject ); 8681 OString aExtName( aObjName.makeStringAndClear() ); 8682 8683 OStringBuffer aLine( 80 ); 8684 // insert XObject 8685 aLine.append( "q /" ); 8686 aLine.append( aExtName ); 8687 aLine.append( " gs /" ); 8688 aLine.append( aTrName ); 8689 aLine.append( " Do Q\n" ); 8690 writeBuffer( aLine.getStr(), aLine.getLength() ); 8691 8692 pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject ); 8693 pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject ); 8694 } 8695 } 8696 8697 void PDFWriterImpl::endTransparencyGroup( const Rectangle& rBoundingBox, const Bitmap& rAlphaMask ) 8698 { 8699 if( m_aContext.Version >= PDFWriter::PDF_1_4 ) 8700 { 8701 // create XObject 8702 m_aTransparentObjects.push_back( TransparencyEmit() ); 8703 m_aTransparentObjects.back().m_aBoundRect = rBoundingBox; 8704 // convert rectangle to default user space 8705 m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect ); 8706 m_aTransparentObjects.back().m_nObject = createObject(); 8707 m_aTransparentObjects.back().m_fAlpha = 0.0; 8708 // get XObject's content stream 8709 m_aTransparentObjects.back().m_pContentStream = static_cast<SvMemoryStream*>(endRedirect()); 8710 m_aTransparentObjects.back().m_nExtGStateObject = createObject(); 8711 8712 // draw soft mask 8713 beginRedirect( new SvMemoryStream( 1024, 1024 ), Rectangle() ); 8714 drawBitmap( rBoundingBox.TopLeft(), rBoundingBox.GetSize(), rAlphaMask ); 8715 m_aTransparentObjects.back().m_pSoftMaskStream = static_cast<SvMemoryStream*>(endRedirect()); 8716 8717 OStringBuffer aObjName( 16 ); 8718 aObjName.append( "Tr" ); 8719 aObjName.append( m_aTransparentObjects.back().m_nObject ); 8720 OString aTrName( aObjName.makeStringAndClear() ); 8721 aObjName.append( "EGS" ); 8722 aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject ); 8723 OString aExtName( aObjName.makeStringAndClear() ); 8724 8725 OStringBuffer aLine( 80 ); 8726 // insert XObject 8727 aLine.append( "q /" ); 8728 aLine.append( aExtName ); 8729 aLine.append( " gs /" ); 8730 aLine.append( aTrName ); 8731 aLine.append( " Do Q\n" ); 8732 writeBuffer( aLine.getStr(), aLine.getLength() ); 8733 8734 pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject ); 8735 pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject ); 8736 } 8737 } 8738 8739 void PDFWriterImpl::drawRectangle( const Rectangle& rRect ) 8740 { 8741 MARK( "drawRectangle" ); 8742 8743 updateGraphicsState(); 8744 8745 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 8746 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 8747 return; 8748 8749 OStringBuffer aLine( 40 ); 8750 m_aPages.back().appendRect( rRect, aLine ); 8751 8752 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && 8753 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) 8754 aLine.append( " B*\n" ); 8755 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 8756 aLine.append( " S\n" ); 8757 else 8758 aLine.append( " f*\n" ); 8759 8760 writeBuffer( aLine.getStr(), aLine.getLength() ); 8761 } 8762 8763 void PDFWriterImpl::drawRectangle( const Rectangle& rRect, sal_uInt32 nHorzRound, sal_uInt32 nVertRound ) 8764 { 8765 MARK( "drawRectangle with rounded edges" ); 8766 8767 if( !nHorzRound && !nVertRound ) 8768 drawRectangle( rRect ); 8769 8770 updateGraphicsState(); 8771 8772 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 8773 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 8774 return; 8775 8776 if( nHorzRound > (sal_uInt32)rRect.GetWidth()/2 ) 8777 nHorzRound = rRect.GetWidth()/2; 8778 if( nVertRound > (sal_uInt32)rRect.GetHeight()/2 ) 8779 nVertRound = rRect.GetHeight()/2; 8780 8781 Point aPoints[16]; 8782 const double kappa = 0.5522847498; 8783 const sal_uInt32 kx = (sal_uInt32)((kappa*(double)nHorzRound)+0.5); 8784 const sal_uInt32 ky = (sal_uInt32)((kappa*(double)nVertRound)+0.5); 8785 8786 aPoints[1] = Point( rRect.TopLeft().X() + nHorzRound, rRect.TopLeft().Y() ); 8787 aPoints[0] = Point( aPoints[1].X() - kx, aPoints[1].Y() ); 8788 aPoints[2] = Point( rRect.TopRight().X()+1 - nHorzRound, aPoints[1].Y() ); 8789 aPoints[3] = Point( aPoints[2].X()+kx, aPoints[2].Y() ); 8790 8791 aPoints[5] = Point( rRect.TopRight().X()+1, rRect.TopRight().Y()+nVertRound ); 8792 aPoints[4] = Point( aPoints[5].X(), aPoints[5].Y()-ky ); 8793 aPoints[6] = Point( aPoints[5].X(), rRect.BottomRight().Y()+1 - nVertRound ); 8794 aPoints[7] = Point( aPoints[6].X(), aPoints[6].Y()+ky ); 8795 8796 aPoints[9] = Point( rRect.BottomRight().X()+1-nHorzRound, rRect.BottomRight().Y()+1 ); 8797 aPoints[8] = Point( aPoints[9].X()+kx, aPoints[9].Y() ); 8798 aPoints[10] = Point( rRect.BottomLeft().X() + nHorzRound, aPoints[9].Y() ); 8799 aPoints[11] = Point( aPoints[10].X()-kx, aPoints[10].Y() ); 8800 8801 aPoints[13] = Point( rRect.BottomLeft().X(), rRect.BottomLeft().Y()+1-nVertRound ); 8802 aPoints[12] = Point( aPoints[13].X(), aPoints[13].Y()+ky ); 8803 aPoints[14] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y()+nVertRound ); 8804 aPoints[15] = Point( aPoints[14].X(), aPoints[14].Y()-ky ); 8805 8806 8807 OStringBuffer aLine( 80 ); 8808 m_aPages.back().appendPoint( aPoints[1], aLine ); 8809 aLine.append( " m " ); 8810 m_aPages.back().appendPoint( aPoints[2], aLine ); 8811 aLine.append( " l " ); 8812 m_aPages.back().appendPoint( aPoints[3], aLine ); 8813 aLine.append( ' ' ); 8814 m_aPages.back().appendPoint( aPoints[4], aLine ); 8815 aLine.append( ' ' ); 8816 m_aPages.back().appendPoint( aPoints[5], aLine ); 8817 aLine.append( " c\n" ); 8818 m_aPages.back().appendPoint( aPoints[6], aLine ); 8819 aLine.append( " l " ); 8820 m_aPages.back().appendPoint( aPoints[7], aLine ); 8821 aLine.append( ' ' ); 8822 m_aPages.back().appendPoint( aPoints[8], aLine ); 8823 aLine.append( ' ' ); 8824 m_aPages.back().appendPoint( aPoints[9], aLine ); 8825 aLine.append( " c\n" ); 8826 m_aPages.back().appendPoint( aPoints[10], aLine ); 8827 aLine.append( " l " ); 8828 m_aPages.back().appendPoint( aPoints[11], aLine ); 8829 aLine.append( ' ' ); 8830 m_aPages.back().appendPoint( aPoints[12], aLine ); 8831 aLine.append( ' ' ); 8832 m_aPages.back().appendPoint( aPoints[13], aLine ); 8833 aLine.append( " c\n" ); 8834 m_aPages.back().appendPoint( aPoints[14], aLine ); 8835 aLine.append( " l " ); 8836 m_aPages.back().appendPoint( aPoints[15], aLine ); 8837 aLine.append( ' ' ); 8838 m_aPages.back().appendPoint( aPoints[0], aLine ); 8839 aLine.append( ' ' ); 8840 m_aPages.back().appendPoint( aPoints[1], aLine ); 8841 aLine.append( " c " ); 8842 8843 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && 8844 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) 8845 aLine.append( "b*\n" ); 8846 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 8847 aLine.append( "s\n" ); 8848 else 8849 aLine.append( "f*\n" ); 8850 8851 writeBuffer( aLine.getStr(), aLine.getLength() ); 8852 } 8853 8854 void PDFWriterImpl::drawEllipse( const Rectangle& rRect ) 8855 { 8856 MARK( "drawEllipse" ); 8857 8858 updateGraphicsState(); 8859 8860 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 8861 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 8862 return; 8863 8864 Point aPoints[12]; 8865 const double kappa = 0.5522847498; 8866 const sal_uInt32 kx = (sal_uInt32)((kappa*(double)rRect.GetWidth()/2.0)+0.5); 8867 const sal_uInt32 ky = (sal_uInt32)((kappa*(double)rRect.GetHeight()/2.0)+0.5); 8868 8869 aPoints[1] = Point( rRect.TopLeft().X() + rRect.GetWidth()/2, rRect.TopLeft().Y() ); 8870 aPoints[0] = Point( aPoints[1].X() - kx, aPoints[1].Y() ); 8871 aPoints[2] = Point( aPoints[1].X() + kx, aPoints[1].Y() ); 8872 8873 aPoints[4] = Point( rRect.TopRight().X()+1, rRect.TopRight().Y() + rRect.GetHeight()/2 ); 8874 aPoints[3] = Point( aPoints[4].X(), aPoints[4].Y() - ky ); 8875 aPoints[5] = Point( aPoints[4].X(), aPoints[4].Y() + ky ); 8876 8877 aPoints[7] = Point( rRect.BottomLeft().X() + rRect.GetWidth()/2, rRect.BottomLeft().Y()+1 ); 8878 aPoints[6] = Point( aPoints[7].X() + kx, aPoints[7].Y() ); 8879 aPoints[8] = Point( aPoints[7].X() - kx, aPoints[7].Y() ); 8880 8881 aPoints[10] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y() + rRect.GetHeight()/2 ); 8882 aPoints[9] = Point( aPoints[10].X(), aPoints[10].Y() + ky ); 8883 aPoints[11] = Point( aPoints[10].X(), aPoints[10].Y() - ky ); 8884 8885 OStringBuffer aLine( 80 ); 8886 m_aPages.back().appendPoint( aPoints[1], aLine ); 8887 aLine.append( " m " ); 8888 m_aPages.back().appendPoint( aPoints[2], aLine ); 8889 aLine.append( ' ' ); 8890 m_aPages.back().appendPoint( aPoints[3], aLine ); 8891 aLine.append( ' ' ); 8892 m_aPages.back().appendPoint( aPoints[4], aLine ); 8893 aLine.append( " c\n" ); 8894 m_aPages.back().appendPoint( aPoints[5], aLine ); 8895 aLine.append( ' ' ); 8896 m_aPages.back().appendPoint( aPoints[6], aLine ); 8897 aLine.append( ' ' ); 8898 m_aPages.back().appendPoint( aPoints[7], aLine ); 8899 aLine.append( " c\n" ); 8900 m_aPages.back().appendPoint( aPoints[8], aLine ); 8901 aLine.append( ' ' ); 8902 m_aPages.back().appendPoint( aPoints[9], aLine ); 8903 aLine.append( ' ' ); 8904 m_aPages.back().appendPoint( aPoints[10], aLine ); 8905 aLine.append( " c\n" ); 8906 m_aPages.back().appendPoint( aPoints[11], aLine ); 8907 aLine.append( ' ' ); 8908 m_aPages.back().appendPoint( aPoints[0], aLine ); 8909 aLine.append( ' ' ); 8910 m_aPages.back().appendPoint( aPoints[1], aLine ); 8911 aLine.append( " c " ); 8912 8913 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && 8914 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) 8915 aLine.append( "b*\n" ); 8916 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 8917 aLine.append( "s\n" ); 8918 else 8919 aLine.append( "f*\n" ); 8920 8921 writeBuffer( aLine.getStr(), aLine.getLength() ); 8922 } 8923 8924 static double calcAngle( const Rectangle& rRect, const Point& rPoint ) 8925 { 8926 Point aOrigin((rRect.Left()+rRect.Right()+1)/2, 8927 (rRect.Top()+rRect.Bottom()+1)/2); 8928 Point aPoint = rPoint - aOrigin; 8929 8930 double fX = (double)aPoint.X(); 8931 double fY = (double)-aPoint.Y(); 8932 8933 if( rRect.GetWidth() > rRect.GetHeight() ) 8934 fY = fY*((double)rRect.GetWidth()/(double)rRect.GetHeight()); 8935 else if( rRect.GetHeight() > rRect.GetWidth() ) 8936 fX = fX*((double)rRect.GetHeight()/(double)rRect.GetWidth()); 8937 return atan2( fY, fX ); 8938 } 8939 8940 void PDFWriterImpl::drawArc( const Rectangle& rRect, const Point& rStart, const Point& rStop, bool bWithPie, bool bWithChord ) 8941 { 8942 MARK( "drawArc" ); 8943 8944 updateGraphicsState(); 8945 8946 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 8947 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 8948 return; 8949 8950 // calculate start and stop angles 8951 const double fStartAngle = calcAngle( rRect, rStart ); 8952 double fStopAngle = calcAngle( rRect, rStop ); 8953 while( fStopAngle < fStartAngle ) 8954 fStopAngle += 2.0*M_PI; 8955 const int nFragments = (int)((fStopAngle-fStartAngle)/(M_PI/2.0))+1; 8956 const double fFragmentDelta = (fStopAngle-fStartAngle)/(double)nFragments; 8957 const double kappa = fabs( 4.0 * (1.0-cos(fFragmentDelta/2.0))/sin(fFragmentDelta/2.0) / 3.0); 8958 const double halfWidth = (double)rRect.GetWidth()/2.0; 8959 const double halfHeight = (double)rRect.GetHeight()/2.0; 8960 8961 const Point aCenter( (rRect.Left()+rRect.Right()+1)/2, 8962 (rRect.Top()+rRect.Bottom()+1)/2 ); 8963 8964 OStringBuffer aLine( 30*nFragments ); 8965 Point aPoint( (int)(halfWidth * cos(fStartAngle) ), 8966 -(int)(halfHeight * sin(fStartAngle) ) ); 8967 aPoint += aCenter; 8968 m_aPages.back().appendPoint( aPoint, aLine ); 8969 aLine.append( " m " ); 8970 if( !basegfx::fTools::equal(fStartAngle, fStopAngle) ) 8971 { 8972 for( int i = 0; i < nFragments; i++ ) 8973 { 8974 const double fStartFragment = fStartAngle + (double)i*fFragmentDelta; 8975 const double fStopFragment = fStartFragment + fFragmentDelta; 8976 aPoint = Point( (int)(halfWidth * (cos(fStartFragment) - kappa*sin(fStartFragment) ) ), 8977 -(int)(halfHeight * (sin(fStartFragment) + kappa*cos(fStartFragment) ) ) ); 8978 aPoint += aCenter; 8979 m_aPages.back().appendPoint( aPoint, aLine ); 8980 aLine.append( ' ' ); 8981 8982 aPoint = Point( (int)(halfWidth * (cos(fStopFragment) + kappa*sin(fStopFragment) ) ), 8983 -(int)(halfHeight * (sin(fStopFragment) - kappa*cos(fStopFragment) ) ) ); 8984 aPoint += aCenter; 8985 m_aPages.back().appendPoint( aPoint, aLine ); 8986 aLine.append( ' ' ); 8987 8988 aPoint = Point( (int)(halfWidth * cos(fStopFragment) ), 8989 -(int)(halfHeight * sin(fStopFragment) ) ); 8990 aPoint += aCenter; 8991 m_aPages.back().appendPoint( aPoint, aLine ); 8992 aLine.append( " c\n" ); 8993 } 8994 } 8995 if( bWithChord || bWithPie ) 8996 { 8997 if( bWithPie ) 8998 { 8999 m_aPages.back().appendPoint( aCenter, aLine ); 9000 aLine.append( " l " ); 9001 } 9002 aLine.append( "h " ); 9003 } 9004 if( ! bWithChord && ! bWithPie ) 9005 aLine.append( "S\n" ); 9006 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && 9007 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) 9008 aLine.append( "B*\n" ); 9009 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 9010 aLine.append( "S\n" ); 9011 else 9012 aLine.append( "f*\n" ); 9013 9014 writeBuffer( aLine.getStr(), aLine.getLength() ); 9015 } 9016 9017 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly ) 9018 { 9019 MARK( "drawPolyLine" ); 9020 9021 sal_uInt16 nPoints = rPoly.GetSize(); 9022 if( nPoints < 2 ) 9023 return; 9024 9025 updateGraphicsState(); 9026 9027 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) 9028 return; 9029 9030 OStringBuffer aLine( 20 * nPoints ); 9031 m_aPages.back().appendPolygon( rPoly, aLine, rPoly[0] == rPoly[nPoints-1] ); 9032 aLine.append( "S\n" ); 9033 9034 writeBuffer( aLine.getStr(), aLine.getLength() ); 9035 } 9036 9037 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const LineInfo& rInfo ) 9038 { 9039 MARK( "drawPolyLine with LineInfo" ); 9040 9041 updateGraphicsState(); 9042 9043 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) 9044 return; 9045 9046 OStringBuffer aLine; 9047 aLine.append( "q " ); 9048 if( m_aPages.back().appendLineInfo( rInfo, aLine ) ) 9049 { 9050 writeBuffer( aLine.getStr(), aLine.getLength() ); 9051 drawPolyLine( rPoly ); 9052 writeBuffer( "Q\n", 2 ); 9053 } 9054 else 9055 { 9056 PDFWriter::ExtLineInfo aInfo; 9057 convertLineInfoToExtLineInfo( rInfo, aInfo ); 9058 drawPolyLine( rPoly, aInfo ); 9059 } 9060 } 9061 9062 void PDFWriterImpl::convertLineInfoToExtLineInfo( const LineInfo& rIn, PDFWriter::ExtLineInfo& rOut ) 9063 { 9064 DBG_ASSERT( rIn.GetStyle() == LINE_DASH, "invalid conversion" ); 9065 rOut.m_fLineWidth = rIn.GetWidth(); 9066 rOut.m_fTransparency = 0.0; 9067 rOut.m_eCap = PDFWriter::capButt; 9068 rOut.m_eJoin = PDFWriter::joinMiter; 9069 rOut.m_fMiterLimit = 10; 9070 rOut.m_aDashArray.clear(); 9071 9072 // add DashDot to DashArray 9073 const int nDashes = rIn.GetDashCount(); 9074 const int nDashLen = rIn.GetDashLen(); 9075 const int nDistance = rIn.GetDistance(); 9076 9077 for( int n = 0; n < nDashes; n++ ) 9078 { 9079 rOut.m_aDashArray.push_back( nDashLen ); 9080 rOut.m_aDashArray.push_back( nDistance ); 9081 } 9082 9083 const int nDots = rIn.GetDotCount(); 9084 const int nDotLen = rIn.GetDotLen(); 9085 9086 for( int n = 0; n < nDots; n++ ) 9087 { 9088 rOut.m_aDashArray.push_back( nDotLen ); 9089 rOut.m_aDashArray.push_back( nDistance ); 9090 } 9091 9092 // add LineJoin 9093 switch(rIn.GetLineJoin()) 9094 { 9095 case basegfx::B2DLINEJOIN_BEVEL : 9096 { 9097 rOut.m_eJoin = PDFWriter::joinBevel; 9098 break; 9099 } 9100 default : // basegfx::B2DLINEJOIN_NONE : 9101 // Pdf has no 'none' lineJoin, default is miter 9102 case basegfx::B2DLINEJOIN_MIDDLE : 9103 case basegfx::B2DLINEJOIN_MITER : 9104 { 9105 rOut.m_eJoin = PDFWriter::joinMiter; 9106 break; 9107 } 9108 case basegfx::B2DLINEJOIN_ROUND : 9109 { 9110 rOut.m_eJoin = PDFWriter::joinRound; 9111 break; 9112 } 9113 } 9114 9115 // add LineCap 9116 switch(rIn.GetLineCap()) 9117 { 9118 default: /* com::sun::star::drawing::LineCap_BUTT */ 9119 { 9120 rOut.m_eCap = PDFWriter::capButt; 9121 break; 9122 } 9123 case com::sun::star::drawing::LineCap_ROUND: 9124 { 9125 rOut.m_eCap = PDFWriter::capRound; 9126 break; 9127 } 9128 case com::sun::star::drawing::LineCap_SQUARE: 9129 { 9130 rOut.m_eCap = PDFWriter::capSquare; 9131 break; 9132 } 9133 } 9134 } 9135 9136 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const PDFWriter::ExtLineInfo& rInfo ) 9137 { 9138 MARK( "drawPolyLine with ExtLineInfo" ); 9139 9140 updateGraphicsState(); 9141 9142 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) 9143 return; 9144 9145 if( rInfo.m_fTransparency >= 1.0 ) 9146 return; 9147 9148 if( rInfo.m_fTransparency != 0.0 ) 9149 beginTransparencyGroup(); 9150 9151 OStringBuffer aLine; 9152 aLine.append( "q " ); 9153 m_aPages.back().appendMappedLength( rInfo.m_fLineWidth, aLine ); 9154 aLine.append( " w" ); 9155 if( rInfo.m_aDashArray.size() < 10 ) // implmentation limit of acrobat reader 9156 { 9157 switch( rInfo.m_eCap ) 9158 { 9159 default: 9160 case PDFWriter::capButt: aLine.append( " 0 J" );break; 9161 case PDFWriter::capRound: aLine.append( " 1 J" );break; 9162 case PDFWriter::capSquare: aLine.append( " 2 J" );break; 9163 } 9164 switch( rInfo.m_eJoin ) 9165 { 9166 default: 9167 case PDFWriter::joinMiter: 9168 { 9169 double fLimit = rInfo.m_fMiterLimit; 9170 if( rInfo.m_fLineWidth < rInfo.m_fMiterLimit ) 9171 fLimit = fLimit / rInfo.m_fLineWidth; 9172 if( fLimit < 1.0 ) 9173 fLimit = 1.0; 9174 aLine.append( " 0 j " ); 9175 appendDouble( fLimit, aLine ); 9176 aLine.append( " M" ); 9177 } 9178 break; 9179 case PDFWriter::joinRound: aLine.append( " 1 j" );break; 9180 case PDFWriter::joinBevel: aLine.append( " 2 j" );break; 9181 } 9182 if( rInfo.m_aDashArray.size() > 0 ) 9183 { 9184 aLine.append( " [ " ); 9185 for( std::vector<double>::const_iterator it = rInfo.m_aDashArray.begin(); 9186 it != rInfo.m_aDashArray.end(); ++it ) 9187 { 9188 m_aPages.back().appendMappedLength( *it, aLine ); 9189 aLine.append( ' ' ); 9190 } 9191 aLine.append( "] 0 d" ); 9192 } 9193 aLine.append( "\n" ); 9194 writeBuffer( aLine.getStr(), aLine.getLength() ); 9195 drawPolyLine( rPoly ); 9196 } 9197 else 9198 { 9199 basegfx::B2DPolygon aPoly(rPoly.getB2DPolygon()); 9200 basegfx::B2DPolyPolygon aPolyPoly; 9201 9202 basegfx::tools::applyLineDashing(aPoly, rInfo.m_aDashArray, &aPolyPoly); 9203 9204 // Old applyLineDashing subdivided the polygon. New one will create bezier curve segments. 9205 // To mimic old behaviour, apply subdivide here. If beziers shall be written (better quality) 9206 // this line needs to be removed and the loop below adapted accordingly 9207 aPolyPoly = basegfx::tools::adaptiveSubdivideByAngle(aPolyPoly); 9208 9209 const sal_uInt32 nPolygonCount(aPolyPoly.count()); 9210 9211 for( sal_uInt32 nPoly = 0; nPoly < nPolygonCount; nPoly++ ) 9212 { 9213 aLine.append( (nPoly != 0 && (nPoly & 7) == 0) ? "\n" : " " ); 9214 aPoly = aPolyPoly.getB2DPolygon( nPoly ); 9215 const sal_uInt32 nPointCount(aPoly.count()); 9216 9217 if(nPointCount) 9218 { 9219 const sal_uInt32 nEdgeCount(aPoly.isClosed() ? nPointCount : nPointCount - 1); 9220 basegfx::B2DPoint aCurrent(aPoly.getB2DPoint(0)); 9221 9222 for(sal_uInt32 a(0); a < nEdgeCount; a++) 9223 { 9224 if( a > 0 ) 9225 aLine.append( " " ); 9226 const sal_uInt32 nNextIndex((a + 1) % nPointCount); 9227 const basegfx::B2DPoint aNext(aPoly.getB2DPoint(nNextIndex)); 9228 9229 m_aPages.back().appendPoint( Point( FRound(aCurrent.getX()), 9230 FRound(aCurrent.getY()) ), 9231 aLine ); 9232 aLine.append( " m " ); 9233 m_aPages.back().appendPoint( Point( FRound(aNext.getX()), 9234 FRound(aNext.getY()) ), 9235 aLine ); 9236 aLine.append( " l" ); 9237 9238 // prepare next edge 9239 aCurrent = aNext; 9240 } 9241 } 9242 } 9243 aLine.append( " S " ); 9244 writeBuffer( aLine.getStr(), aLine.getLength() ); 9245 } 9246 writeBuffer( "Q\n", 2 ); 9247 9248 if( rInfo.m_fTransparency != 0.0 ) 9249 { 9250 // FIXME: actually this may be incorrect with bezier polygons 9251 Rectangle aBoundRect( rPoly.GetBoundRect() ); 9252 // avoid clipping with thick lines 9253 if( rInfo.m_fLineWidth > 0.0 ) 9254 { 9255 sal_Int32 nLW = sal_Int32(rInfo.m_fLineWidth); 9256 aBoundRect.Top() -= nLW; 9257 aBoundRect.Left() -= nLW; 9258 aBoundRect.Right() += nLW; 9259 aBoundRect.Bottom() += nLW; 9260 } 9261 endTransparencyGroup( aBoundRect, (sal_uInt16)(100.0*rInfo.m_fTransparency) ); 9262 } 9263 } 9264 9265 void PDFWriterImpl::drawPixel( const Point& rPoint, const Color& rColor ) 9266 { 9267 MARK( "drawPixel" ); 9268 9269 Color aColor = ( rColor == Color( COL_TRANSPARENT ) ? m_aGraphicsStack.front().m_aLineColor : rColor ); 9270 9271 if( aColor == Color( COL_TRANSPARENT ) ) 9272 return; 9273 9274 // pixels are drawn in line color, so have to set 9275 // the nonstroking color to line color 9276 Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor; 9277 setFillColor( aColor ); 9278 9279 updateGraphicsState(); 9280 9281 OStringBuffer aLine( 20 ); 9282 m_aPages.back().appendPoint( rPoint, aLine ); 9283 aLine.append( ' ' ); 9284 appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIX()), aLine ); 9285 aLine.append( ' ' ); 9286 appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIY()), aLine ); 9287 aLine.append( " re f\n" ); 9288 writeBuffer( aLine.getStr(), aLine.getLength() ); 9289 9290 setFillColor( aOldFillColor ); 9291 } 9292 9293 void PDFWriterImpl::drawPixel( const Polygon& rPoints, const Color* pColors ) 9294 { 9295 MARK( "drawPixel with Polygon" ); 9296 9297 updateGraphicsState(); 9298 9299 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && ! pColors ) 9300 return; 9301 9302 sal_uInt16 nPoints = rPoints.GetSize(); 9303 OStringBuffer aLine( nPoints*40 ); 9304 aLine.append( "q " ); 9305 if( ! pColors ) 9306 { 9307 appendNonStrokingColor( m_aGraphicsStack.front().m_aLineColor, aLine ); 9308 aLine.append( ' ' ); 9309 } 9310 9311 OStringBuffer aPixel(32); 9312 aPixel.append( ' ' ); 9313 appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIX()), aPixel ); 9314 aPixel.append( ' ' ); 9315 appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIY()), aPixel ); 9316 OString aPixelStr = aPixel.makeStringAndClear(); 9317 for( sal_uInt16 i = 0; i < nPoints; i++ ) 9318 { 9319 if( pColors ) 9320 { 9321 if( pColors[i] == Color( COL_TRANSPARENT ) ) 9322 continue; 9323 9324 appendNonStrokingColor( pColors[i], aLine ); 9325 aLine.append( ' ' ); 9326 } 9327 m_aPages.back().appendPoint( rPoints[i], aLine ); 9328 aLine.append( aPixelStr ); 9329 aLine.append( " re f\n" ); 9330 } 9331 aLine.append( "Q\n" ); 9332 writeBuffer( aLine.getStr(), aLine.getLength() ); 9333 } 9334 9335 class AccessReleaser 9336 { 9337 BitmapReadAccess* m_pAccess; 9338 public: 9339 AccessReleaser( BitmapReadAccess* pAccess ) : m_pAccess( pAccess ){} 9340 ~AccessReleaser() { delete m_pAccess; } 9341 }; 9342 9343 bool PDFWriterImpl::writeTransparentObject( TransparencyEmit& rObject ) 9344 { 9345 CHECK_RETURN( updateObject( rObject.m_nObject ) ); 9346 9347 bool bFlateFilter = compressStream( rObject.m_pContentStream ); 9348 rObject.m_pContentStream->Seek( STREAM_SEEK_TO_END ); 9349 sal_uLong nSize = rObject.m_pContentStream->Tell(); 9350 rObject.m_pContentStream->Seek( STREAM_SEEK_TO_BEGIN ); 9351 #if OSL_DEBUG_LEVEL > 1 9352 emitComment( "PDFWriterImpl::writeTransparentObject" ); 9353 #endif 9354 OStringBuffer aLine( 512 ); 9355 CHECK_RETURN( updateObject( rObject.m_nObject ) ); 9356 aLine.append( rObject.m_nObject ); 9357 aLine.append( " 0 obj\n" 9358 "<</Type/XObject\n" 9359 "/Subtype/Form\n" 9360 "/BBox[ " ); 9361 appendFixedInt( rObject.m_aBoundRect.Left(), aLine ); 9362 aLine.append( ' ' ); 9363 appendFixedInt( rObject.m_aBoundRect.Top(), aLine ); 9364 aLine.append( ' ' ); 9365 appendFixedInt( rObject.m_aBoundRect.Right(), aLine ); 9366 aLine.append( ' ' ); 9367 appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aLine ); 9368 aLine.append( " ]\n" ); 9369 if( ! rObject.m_pSoftMaskStream ) 9370 { 9371 if( ! m_bIsPDF_A1 ) 9372 { 9373 aLine.append( "/Group<</S/Transparency/CS/DeviceRGB/K true>>\n" ); 9374 } 9375 } 9376 /* #i42884# the PDF reference recommends that each Form XObject 9377 * should have a resource dict; alas if that is the same object 9378 * as the one of the page it triggers an endless recursion in 9379 * acroread 5 (6 and up have that fixed). Since we have only one 9380 * resource dict anyway, let's use the one from the page by NOT 9381 * emitting a Resources entry. 9382 */ 9383 #if 0 9384 aLine.append( " /Resources " ); 9385 aLine.append( getResourceDictObj() ); 9386 aLine.append( " 0 R\n" ); 9387 #endif 9388 9389 aLine.append( "/Length " ); 9390 aLine.append( (sal_Int32)(nSize) ); 9391 aLine.append( "\n" ); 9392 if( bFlateFilter ) 9393 aLine.append( "/Filter/FlateDecode\n" ); 9394 aLine.append( ">>\n" 9395 "stream\n" ); 9396 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9397 checkAndEnableStreamEncryption( rObject.m_nObject ); 9398 CHECK_RETURN( writeBuffer( rObject.m_pContentStream->GetData(), nSize ) ); 9399 disableStreamEncryption(); 9400 aLine.setLength( 0 ); 9401 aLine.append( "\n" 9402 "endstream\n" 9403 "endobj\n\n" ); 9404 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9405 9406 // write ExtGState dict for this XObject 9407 aLine.setLength( 0 ); 9408 aLine.append( rObject.m_nExtGStateObject ); 9409 aLine.append( " 0 obj\n" 9410 "<<" ); 9411 if( ! rObject.m_pSoftMaskStream ) 9412 { 9413 //i59651 9414 if( m_bIsPDF_A1 ) 9415 { 9416 aLine.append( "/CA 1.0/ca 1.0" ); 9417 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA ); 9418 } 9419 else 9420 { 9421 aLine.append( "/CA " ); 9422 appendDouble( rObject.m_fAlpha, aLine ); 9423 aLine.append( "\n" 9424 " /ca " ); 9425 appendDouble( rObject.m_fAlpha, aLine ); 9426 } 9427 aLine.append( "\n" ); 9428 } 9429 else 9430 { 9431 if( m_bIsPDF_A1 ) 9432 { 9433 aLine.append( "/SMask/None" ); 9434 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA ); 9435 } 9436 else 9437 { 9438 rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_END ); 9439 sal_Int32 nMaskSize = (sal_Int32)rObject.m_pSoftMaskStream->Tell(); 9440 rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_BEGIN ); 9441 sal_Int32 nMaskObject = createObject(); 9442 aLine.append( "/SMask<</Type/Mask/S/Luminosity/G " ); 9443 aLine.append( nMaskObject ); 9444 aLine.append( " 0 R>>\n" ); 9445 9446 OStringBuffer aMask; 9447 aMask.append( nMaskObject ); 9448 aMask.append( " 0 obj\n" 9449 "<</Type/XObject\n" 9450 "/Subtype/Form\n" 9451 "/BBox[" ); 9452 appendFixedInt( rObject.m_aBoundRect.Left(), aMask ); 9453 aMask.append( ' ' ); 9454 appendFixedInt( rObject.m_aBoundRect.Top(), aMask ); 9455 aMask.append( ' ' ); 9456 appendFixedInt( rObject.m_aBoundRect.Right(), aMask ); 9457 aMask.append( ' ' ); 9458 appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aMask ); 9459 aMask.append( "]\n" ); 9460 9461 /* #i42884# see above */ 9462 #if 0 9463 aLine.append( "/Resources " ); 9464 aMask.append( getResourceDictObj() ); 9465 aMask.append( " 0 R\n" ); 9466 #endif 9467 9468 aMask.append( "/Group<</S/Transparency/CS/DeviceRGB>>\n" ); 9469 aMask.append( "/Length " ); 9470 aMask.append( nMaskSize ); 9471 aMask.append( ">>\n" 9472 "stream\n" ); 9473 CHECK_RETURN( updateObject( nMaskObject ) ); 9474 checkAndEnableStreamEncryption( nMaskObject ); 9475 CHECK_RETURN( writeBuffer( aMask.getStr(), aMask.getLength() ) ); 9476 CHECK_RETURN( writeBuffer( rObject.m_pSoftMaskStream->GetData(), nMaskSize ) ); 9477 disableStreamEncryption(); 9478 aMask.setLength( 0 ); 9479 aMask.append( "\nendstream\n" 9480 "endobj\n\n" ); 9481 CHECK_RETURN( writeBuffer( aMask.getStr(), aMask.getLength() ) ); 9482 } 9483 } 9484 aLine.append( ">>\n" 9485 "endobj\n\n" ); 9486 CHECK_RETURN( updateObject( rObject.m_nExtGStateObject ) ); 9487 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9488 9489 return true; 9490 } 9491 9492 bool PDFWriterImpl::writeGradientFunction( GradientEmit& rObject ) 9493 { 9494 sal_Int32 nFunctionObject = createObject(); 9495 CHECK_RETURN( updateObject( nFunctionObject ) ); 9496 9497 VirtualDevice aDev; 9498 aDev.SetOutputSizePixel( rObject.m_aSize ); 9499 aDev.SetMapMode( MapMode( MAP_PIXEL ) ); 9500 if( m_aContext.ColorMode == PDFWriter::DrawGreyscale ) 9501 aDev.SetDrawMode( aDev.GetDrawMode() | 9502 ( DRAWMODE_GRAYLINE | DRAWMODE_GRAYFILL | DRAWMODE_GRAYTEXT | 9503 DRAWMODE_GRAYBITMAP | DRAWMODE_GRAYGRADIENT ) ); 9504 aDev.DrawGradient( Rectangle( Point( 0, 0 ), rObject.m_aSize ), rObject.m_aGradient ); 9505 9506 Bitmap aSample = aDev.GetBitmap( Point( 0, 0 ), rObject.m_aSize ); 9507 BitmapReadAccess* pAccess = aSample.AcquireReadAccess(); 9508 AccessReleaser aReleaser( pAccess ); 9509 9510 Size aSize = aSample.GetSizePixel(); 9511 9512 sal_Int32 nStreamLengthObject = createObject(); 9513 #if OSL_DEBUG_LEVEL > 1 9514 emitComment( "PDFWriterImpl::writeGradientFunction" ); 9515 #endif 9516 OStringBuffer aLine( 120 ); 9517 aLine.append( nFunctionObject ); 9518 aLine.append( " 0 obj\n" 9519 "<</FunctionType 0\n" 9520 "/Domain[ 0 1 0 1 ]\n" 9521 "/Size[ " ); 9522 aLine.append( (sal_Int32)aSize.Width() ); 9523 aLine.append( ' ' ); 9524 aLine.append( (sal_Int32)aSize.Height() ); 9525 aLine.append( " ]\n" 9526 "/BitsPerSample 8\n" 9527 "/Range[ 0 1 0 1 0 1 ]\n" 9528 "/Order 3\n" 9529 "/Length " ); 9530 aLine.append( nStreamLengthObject ); 9531 aLine.append( " 0 R\n" 9532 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 9533 "/Filter/FlateDecode" 9534 #endif 9535 ">>\n" 9536 "stream\n" ); 9537 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9538 9539 sal_uInt64 nStartStreamPos = 0; 9540 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartStreamPos )) ); 9541 9542 checkAndEnableStreamEncryption( nFunctionObject ); 9543 beginCompression(); 9544 for( int y = aSize.Height()-1; y >= 0; y-- ) 9545 { 9546 for( int x = 0; x < aSize.Width(); x++ ) 9547 { 9548 sal_uInt8 aCol[3]; 9549 BitmapColor aColor = pAccess->GetColor( y, x ); 9550 aCol[0] = aColor.GetRed(); 9551 aCol[1] = aColor.GetGreen(); 9552 aCol[2] = aColor.GetBlue(); 9553 CHECK_RETURN( writeBuffer( aCol, 3 ) ); 9554 } 9555 } 9556 endCompression(); 9557 disableStreamEncryption(); 9558 9559 sal_uInt64 nEndStreamPos = 0; 9560 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndStreamPos )) ); 9561 9562 aLine.setLength( 0 ); 9563 aLine.append( "\nendstream\nendobj\n\n" ); 9564 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9565 9566 // write stream length 9567 CHECK_RETURN( updateObject( nStreamLengthObject ) ); 9568 aLine.setLength( 0 ); 9569 aLine.append( nStreamLengthObject ); 9570 aLine.append( " 0 obj\n" ); 9571 aLine.append( (sal_Int64)(nEndStreamPos-nStartStreamPos) ); 9572 aLine.append( "\nendobj\n\n" ); 9573 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9574 9575 CHECK_RETURN( updateObject( rObject.m_nObject ) ); 9576 aLine.setLength( 0 ); 9577 aLine.append( rObject.m_nObject ); 9578 aLine.append( " 0 obj\n" 9579 "<</ShadingType 1\n" 9580 "/ColorSpace/DeviceRGB\n" 9581 "/AntiAlias true\n" 9582 "/Domain[ 0 1 0 1 ]\n" 9583 "/Matrix[ " ); 9584 aLine.append( (sal_Int32)aSize.Width() ); 9585 aLine.append( " 0 0 " ); 9586 aLine.append( (sal_Int32)aSize.Height() ); 9587 aLine.append( " 0 0 ]\n" 9588 "/Function " ); 9589 aLine.append( nFunctionObject ); 9590 aLine.append( " 0 R\n" 9591 ">>\n" 9592 "endobj\n\n" ); 9593 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9594 9595 return true; 9596 } 9597 9598 bool PDFWriterImpl::writeJPG( JPGEmit& rObject ) 9599 { 9600 CHECK_RETURN( rObject.m_pStream ); 9601 CHECK_RETURN( updateObject( rObject.m_nObject ) ); 9602 9603 sal_Int32 nLength = 0; 9604 rObject.m_pStream->Seek( STREAM_SEEK_TO_END ); 9605 nLength = rObject.m_pStream->Tell(); 9606 rObject.m_pStream->Seek( STREAM_SEEK_TO_BEGIN ); 9607 9608 sal_Int32 nMaskObject = 0; 9609 if( !!rObject.m_aMask ) 9610 { 9611 if( rObject.m_aMask.GetBitCount() == 1 || 9612 ( rObject.m_aMask.GetBitCount() == 8 && m_aContext.Version >= PDFWriter::PDF_1_4 && !m_bIsPDF_A1 )//i59651 9613 ) 9614 { 9615 nMaskObject = createObject(); 9616 } 9617 else if( m_bIsPDF_A1 ) 9618 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA ); 9619 else if( m_aContext.Version < PDFWriter::PDF_1_4 ) 9620 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDF13 ); 9621 9622 } 9623 #if OSL_DEBUG_LEVEL > 1 9624 emitComment( "PDFWriterImpl::writeJPG" ); 9625 #endif 9626 9627 OStringBuffer aLine(200); 9628 aLine.append( rObject.m_nObject ); 9629 aLine.append( " 0 obj\n" 9630 "<</Type/XObject/Subtype/Image/Width " ); 9631 aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Width() ); 9632 aLine.append( " /Height " ); 9633 aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Height() ); 9634 aLine.append( " /BitsPerComponent 8 " ); 9635 if( rObject.m_bTrueColor ) 9636 aLine.append( "/ColorSpace/DeviceRGB" ); 9637 else 9638 aLine.append( "/ColorSpace/DeviceGray" ); 9639 aLine.append( "/Filter/DCTDecode/Length " ); 9640 aLine.append( nLength ); 9641 if( nMaskObject ) 9642 { 9643 aLine.append( rObject.m_aMask.GetBitCount() == 1 ? " /Mask " : " /SMask " ); 9644 aLine.append( nMaskObject ); 9645 aLine.append( " 0 R " ); 9646 } 9647 aLine.append( ">>\nstream\n" ); 9648 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9649 9650 checkAndEnableStreamEncryption( rObject.m_nObject ); 9651 CHECK_RETURN( writeBuffer( rObject.m_pStream->GetData(), nLength ) ); 9652 disableStreamEncryption(); 9653 9654 aLine.setLength( 0 ); 9655 aLine.append( "\nendstream\nendobj\n\n" ); 9656 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9657 9658 if( nMaskObject ) 9659 { 9660 BitmapEmit aEmit; 9661 aEmit.m_nObject = nMaskObject; 9662 if( rObject.m_aMask.GetBitCount() == 1 ) 9663 aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, rObject.m_aMask ); 9664 else if( rObject.m_aMask.GetBitCount() == 8 ) 9665 aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, AlphaMask( rObject.m_aMask ) ); 9666 writeBitmapObject( aEmit, true ); 9667 } 9668 9669 return true; 9670 } 9671 9672 bool PDFWriterImpl::writeBitmapObject( BitmapEmit& rObject, bool bMask ) 9673 { 9674 CHECK_RETURN( updateObject( rObject.m_nObject ) ); 9675 9676 Bitmap aBitmap; 9677 Color aTransparentColor( COL_TRANSPARENT ); 9678 bool bWriteMask = false; 9679 if( ! bMask ) 9680 { 9681 aBitmap = rObject.m_aBitmap.GetBitmap(); 9682 if( rObject.m_aBitmap.IsAlpha() ) 9683 { 9684 if( m_aContext.Version >= PDFWriter::PDF_1_4 ) 9685 bWriteMask = true; 9686 // else draw without alpha channel 9687 } 9688 else 9689 { 9690 switch( rObject.m_aBitmap.GetTransparentType() ) 9691 { 9692 case TRANSPARENT_NONE: 9693 // comes from drawMask function 9694 if( aBitmap.GetBitCount() == 1 && rObject.m_bDrawMask ) 9695 bMask = true; 9696 break; 9697 case TRANSPARENT_COLOR: 9698 aTransparentColor = rObject.m_aBitmap.GetTransparentColor(); 9699 break; 9700 case TRANSPARENT_BITMAP: 9701 bWriteMask = true; 9702 break; 9703 } 9704 } 9705 } 9706 else 9707 { 9708 if( m_aContext.Version < PDFWriter::PDF_1_4 || ! rObject.m_aBitmap.IsAlpha() ) 9709 { 9710 aBitmap = rObject.m_aBitmap.GetMask(); 9711 aBitmap.Convert( BMP_CONVERSION_1BIT_THRESHOLD ); 9712 DBG_ASSERT( aBitmap.GetBitCount() == 1, "mask conversion failed" ); 9713 } 9714 else if( aBitmap.GetBitCount() != 8 ) 9715 { 9716 aBitmap = rObject.m_aBitmap.GetAlpha().GetBitmap(); 9717 aBitmap.Convert( BMP_CONVERSION_8BIT_GREYS ); 9718 DBG_ASSERT( aBitmap.GetBitCount() == 8, "alpha mask conversion failed" ); 9719 } 9720 } 9721 9722 BitmapReadAccess* pAccess = aBitmap.AcquireReadAccess(); 9723 AccessReleaser aReleaser( pAccess ); 9724 9725 bool bTrueColor; 9726 sal_Int32 nBitsPerComponent; 9727 switch( aBitmap.GetBitCount() ) 9728 { 9729 case 1: 9730 case 2: 9731 case 4: 9732 case 8: 9733 bTrueColor = false; 9734 nBitsPerComponent = aBitmap.GetBitCount(); 9735 break; 9736 default: 9737 bTrueColor = true; 9738 nBitsPerComponent = 8; 9739 break; 9740 } 9741 9742 sal_Int32 nStreamLengthObject = createObject(); 9743 sal_Int32 nMaskObject = 0; 9744 9745 #if OSL_DEBUG_LEVEL > 1 9746 emitComment( "PDFWriterImpl::writeBitmapObject" ); 9747 #endif 9748 OStringBuffer aLine(1024); 9749 aLine.append( rObject.m_nObject ); 9750 aLine.append( " 0 obj\n" 9751 "<</Type/XObject/Subtype/Image/Width " ); 9752 aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() ); 9753 aLine.append( "/Height " ); 9754 aLine.append( (sal_Int32)aBitmap.GetSizePixel().Height() ); 9755 aLine.append( "/BitsPerComponent " ); 9756 aLine.append( nBitsPerComponent ); 9757 aLine.append( "/Length " ); 9758 aLine.append( nStreamLengthObject ); 9759 aLine.append( " 0 R\n" ); 9760 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 9761 if( nBitsPerComponent != 1 ) 9762 { 9763 aLine.append( "/Filter/FlateDecode" ); 9764 } 9765 else 9766 { 9767 aLine.append( "/Filter/CCITTFaxDecode/DecodeParms<</K -1/BlackIs1 true/Columns " ); 9768 aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() ); 9769 aLine.append( ">>\n" ); 9770 } 9771 #endif 9772 if( ! bMask ) 9773 { 9774 aLine.append( "/ColorSpace" ); 9775 if( bTrueColor ) 9776 aLine.append( "/DeviceRGB\n" ); 9777 else if( aBitmap.HasGreyPalette() ) 9778 { 9779 aLine.append( "/DeviceGray\n" ); 9780 if( aBitmap.GetBitCount() == 1 ) 9781 { 9782 // #i47395# 1 bit bitmaps occasionally have an inverted grey palette 9783 sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) ); 9784 DBG_ASSERT( nBlackIndex == 0 || nBlackIndex == 1, "wrong black index" ); 9785 if( nBlackIndex == 1 ) 9786 aLine.append( "/Decode[1 0]\n" ); 9787 } 9788 } 9789 else 9790 { 9791 aLine.append( "[ /Indexed/DeviceRGB " ); 9792 aLine.append( (sal_Int32)(pAccess->GetPaletteEntryCount()-1) ); 9793 aLine.append( "\n<" ); 9794 if( m_aContext.Encryption.Encrypt() ) 9795 { 9796 enableStringEncryption( rObject.m_nObject ); 9797 //check encryption buffer size 9798 if( checkEncryptionBufferSize( pAccess->GetPaletteEntryCount()*3 ) ) 9799 { 9800 int nChar = 0; 9801 //fill the encryption buffer 9802 for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ ) 9803 { 9804 const BitmapColor& rColor = pAccess->GetPaletteColor( i ); 9805 m_pEncryptionBuffer[nChar++] = rColor.GetRed(); 9806 m_pEncryptionBuffer[nChar++] = rColor.GetGreen(); 9807 m_pEncryptionBuffer[nChar++] = rColor.GetBlue(); 9808 } 9809 //encrypt the colorspace lookup table 9810 rtl_cipher_encodeARCFOUR( m_aCipher, m_pEncryptionBuffer, nChar, m_pEncryptionBuffer, nChar ); 9811 //now queue the data for output 9812 nChar = 0; 9813 for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ ) 9814 { 9815 appendHex(m_pEncryptionBuffer[nChar++], aLine ); 9816 appendHex(m_pEncryptionBuffer[nChar++], aLine ); 9817 appendHex(m_pEncryptionBuffer[nChar++], aLine ); 9818 } 9819 } 9820 } 9821 else //no encryption requested (PDF/A-1a program flow drops here) 9822 { 9823 for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ ) 9824 { 9825 const BitmapColor& rColor = pAccess->GetPaletteColor( i ); 9826 appendHex( rColor.GetRed(), aLine ); 9827 appendHex( rColor.GetGreen(), aLine ); 9828 appendHex( rColor.GetBlue(), aLine ); 9829 } 9830 } 9831 aLine.append( ">\n]\n" ); 9832 } 9833 } 9834 else 9835 { 9836 if( aBitmap.GetBitCount() == 1 ) 9837 { 9838 aLine.append( "/ImageMask true\n" ); 9839 sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) ); 9840 DBG_ASSERT( nBlackIndex == 0 || nBlackIndex == 1, "wrong black index" ); 9841 if( nBlackIndex ) 9842 aLine.append( "/Decode[ 1 0 ]\n" ); 9843 else 9844 aLine.append( "/Decode[ 0 1 ]\n" ); 9845 } 9846 else if( aBitmap.GetBitCount() == 8 ) 9847 { 9848 aLine.append( "/ColorSpace/DeviceGray\n" 9849 "/Decode [ 1 0 ]\n" ); 9850 } 9851 } 9852 9853 if( ! bMask && m_aContext.Version > PDFWriter::PDF_1_2 && !m_bIsPDF_A1 )//i59651 9854 { 9855 if( bWriteMask ) 9856 { 9857 nMaskObject = createObject(); 9858 if( rObject.m_aBitmap.IsAlpha() && m_aContext.Version > PDFWriter::PDF_1_3 ) 9859 aLine.append( "/SMask " ); 9860 else 9861 aLine.append( "/Mask " ); 9862 aLine.append( nMaskObject ); 9863 aLine.append( " 0 R\n" ); 9864 } 9865 else if( aTransparentColor != Color( COL_TRANSPARENT ) ) 9866 { 9867 aLine.append( "/Mask[ " ); 9868 if( bTrueColor ) 9869 { 9870 aLine.append( (sal_Int32)aTransparentColor.GetRed() ); 9871 aLine.append( ' ' ); 9872 aLine.append( (sal_Int32)aTransparentColor.GetRed() ); 9873 aLine.append( ' ' ); 9874 aLine.append( (sal_Int32)aTransparentColor.GetGreen() ); 9875 aLine.append( ' ' ); 9876 aLine.append( (sal_Int32)aTransparentColor.GetGreen() ); 9877 aLine.append( ' ' ); 9878 aLine.append( (sal_Int32)aTransparentColor.GetBlue() ); 9879 aLine.append( ' ' ); 9880 aLine.append( (sal_Int32)aTransparentColor.GetBlue() ); 9881 } 9882 else 9883 { 9884 sal_Int32 nIndex = pAccess->GetBestPaletteIndex( BitmapColor( aTransparentColor ) ); 9885 aLine.append( nIndex ); 9886 } 9887 aLine.append( " ]\n" ); 9888 } 9889 } 9890 else if( m_bIsPDF_A1 && (bWriteMask || aTransparentColor != Color( COL_TRANSPARENT )) ) 9891 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA ); 9892 9893 aLine.append( ">>\n" 9894 "stream\n" ); 9895 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9896 sal_uInt64 nStartPos = 0; 9897 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos )) ); 9898 9899 checkAndEnableStreamEncryption( rObject.m_nObject ); 9900 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 9901 if( nBitsPerComponent == 1 ) 9902 { 9903 writeG4Stream( pAccess ); 9904 } 9905 else 9906 #endif 9907 { 9908 beginCompression(); 9909 if( ! bTrueColor || pAccess->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_RGB ) 9910 { 9911 const int nScanLineBytes = 1 + ( pAccess->GetBitCount() * ( pAccess->Width() - 1 ) / 8U ); 9912 9913 for( int i = 0; i < pAccess->Height(); i++ ) 9914 { 9915 CHECK_RETURN( writeBuffer( pAccess->GetScanline( i ), nScanLineBytes ) ); 9916 } 9917 } 9918 else 9919 { 9920 const int nScanLineBytes = pAccess->Width()*3; 9921 boost::shared_array<sal_uInt8> pCol( new sal_uInt8[ nScanLineBytes ] ); 9922 for( int y = 0; y < pAccess->Height(); y++ ) 9923 { 9924 for( int x = 0; x < pAccess->Width(); x++ ) 9925 { 9926 BitmapColor aColor = pAccess->GetColor( y, x ); 9927 pCol[3*x+0] = aColor.GetRed(); 9928 pCol[3*x+1] = aColor.GetGreen(); 9929 pCol[3*x+2] = aColor.GetBlue(); 9930 } 9931 CHECK_RETURN( writeBuffer( pCol.get(), nScanLineBytes ) ); 9932 } 9933 } 9934 endCompression(); 9935 } 9936 disableStreamEncryption(); 9937 9938 sal_uInt64 nEndPos = 0; 9939 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndPos )) ); 9940 aLine.setLength( 0 ); 9941 aLine.append( "\nendstream\nendobj\n\n" ); 9942 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9943 CHECK_RETURN( updateObject( nStreamLengthObject ) ); 9944 aLine.setLength( 0 ); 9945 aLine.append( nStreamLengthObject ); 9946 aLine.append( " 0 obj\n" ); 9947 aLine.append( (sal_Int64)(nEndPos-nStartPos) ); 9948 aLine.append( "\nendobj\n\n" ); 9949 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9950 9951 if( nMaskObject ) 9952 { 9953 BitmapEmit aEmit; 9954 aEmit.m_nObject = nMaskObject; 9955 aEmit.m_aBitmap = rObject.m_aBitmap; 9956 return writeBitmapObject( aEmit, true ); 9957 } 9958 9959 return true; 9960 } 9961 9962 void PDFWriterImpl::drawJPGBitmap( SvStream& rDCTData, bool bIsTrueColor, const Size& rSizePixel, const Rectangle& rTargetArea, const Bitmap& rMask ) 9963 { 9964 MARK( "drawJPGBitmap" ); 9965 9966 OStringBuffer aLine( 80 ); 9967 updateGraphicsState(); 9968 9969 // #i40055# sanity check 9970 if( ! (rTargetArea.GetWidth() && rTargetArea.GetHeight() ) ) 9971 return; 9972 if( ! (rSizePixel.Width() && rSizePixel.Height()) ) 9973 return; 9974 9975 rDCTData.Seek( 0 ); 9976 if( bIsTrueColor && m_aContext.ColorMode == PDFWriter::DrawGreyscale ) 9977 { 9978 // need to convert to grayscale; 9979 // load stream to bitmap and draw the bitmap instead 9980 Graphic aGraphic; 9981 GraphicConverter::Import( rDCTData, aGraphic, CVT_JPG ); 9982 Bitmap aBmp( aGraphic.GetBitmap() ); 9983 if( !!rMask && rMask.GetSizePixel() == aBmp.GetSizePixel() ) 9984 { 9985 BitmapEx aBmpEx( aBmp, rMask ); 9986 drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aBmpEx ); 9987 } 9988 else 9989 drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aBmp ); 9990 return; 9991 } 9992 9993 SvMemoryStream* pStream = new SvMemoryStream; 9994 *pStream << rDCTData; 9995 pStream->Seek( STREAM_SEEK_TO_END ); 9996 9997 BitmapID aID; 9998 aID.m_aPixelSize = rSizePixel; 9999 aID.m_nSize = pStream->Tell(); 10000 pStream->Seek( STREAM_SEEK_TO_BEGIN ); 10001 aID.m_nChecksum = rtl_crc32( 0, pStream->GetData(), aID.m_nSize ); 10002 if( ! rMask.IsEmpty() ) 10003 aID.m_nMaskChecksum = rMask.GetChecksum(); 10004 10005 std::list< JPGEmit >::const_iterator it; 10006 for( it = m_aJPGs.begin(); it != m_aJPGs.end() && ! (aID == it->m_aID); ++it ) 10007 ; 10008 if( it == m_aJPGs.end() ) 10009 { 10010 m_aJPGs.push_front( JPGEmit() ); 10011 JPGEmit& rEmit = m_aJPGs.front(); 10012 rEmit.m_nObject = createObject(); 10013 rEmit.m_aID = aID; 10014 rEmit.m_pStream = pStream; 10015 rEmit.m_bTrueColor = bIsTrueColor; 10016 if( !! rMask && rMask.GetSizePixel() == rSizePixel ) 10017 rEmit.m_aMask = rMask; 10018 10019 it = m_aJPGs.begin(); 10020 } 10021 else 10022 delete pStream; 10023 10024 aLine.append( "q " ); 10025 sal_Int32 nCheckWidth = 0; 10026 m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetWidth(), aLine, false, &nCheckWidth ); 10027 aLine.append( " 0 0 " ); 10028 sal_Int32 nCheckHeight = 0; 10029 m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetHeight(), aLine, true, &nCheckHeight ); 10030 aLine.append( ' ' ); 10031 m_aPages.back().appendPoint( rTargetArea.BottomLeft(), aLine ); 10032 aLine.append( " cm\n/Im" ); 10033 aLine.append( it->m_nObject ); 10034 aLine.append( " Do Q\n" ); 10035 if( nCheckWidth == 0 || nCheckHeight == 0 ) 10036 { 10037 // #i97512# avoid invalid current matrix 10038 aLine.setLength( 0 ); 10039 aLine.append( "\n%jpeg image /Im" ); 10040 aLine.append( it->m_nObject ); 10041 aLine.append( " scaled to zero size, omitted\n" ); 10042 } 10043 writeBuffer( aLine.getStr(), aLine.getLength() ); 10044 10045 OStringBuffer aObjName( 16 ); 10046 aObjName.append( "Im" ); 10047 aObjName.append( it->m_nObject ); 10048 pushResource( ResXObject, aObjName.makeStringAndClear(), it->m_nObject ); 10049 10050 } 10051 10052 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEmit& rBitmap, const Color& rFillColor ) 10053 { 10054 OStringBuffer aLine( 80 ); 10055 updateGraphicsState(); 10056 10057 aLine.append( "q " ); 10058 if( rFillColor != Color( COL_TRANSPARENT ) ) 10059 { 10060 appendNonStrokingColor( rFillColor, aLine ); 10061 aLine.append( ' ' ); 10062 } 10063 sal_Int32 nCheckWidth = 0; 10064 m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Width(), aLine, false, &nCheckWidth ); 10065 aLine.append( " 0 0 " ); 10066 sal_Int32 nCheckHeight = 0; 10067 m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Height(), aLine, true, &nCheckHeight ); 10068 aLine.append( ' ' ); 10069 m_aPages.back().appendPoint( rDestPoint + Point( 0, rDestSize.Height()-1 ), aLine ); 10070 aLine.append( " cm\n/Im" ); 10071 aLine.append( rBitmap.m_nObject ); 10072 aLine.append( " Do Q\n" ); 10073 if( nCheckWidth == 0 || nCheckHeight == 0 ) 10074 { 10075 // #i97512# avoid invalid current matrix 10076 aLine.setLength( 0 ); 10077 aLine.append( "\n%bitmap image /Im" ); 10078 aLine.append( rBitmap.m_nObject ); 10079 aLine.append( " scaled to zero size, omitted\n" ); 10080 } 10081 writeBuffer( aLine.getStr(), aLine.getLength() ); 10082 } 10083 10084 const PDFWriterImpl::BitmapEmit& PDFWriterImpl::createBitmapEmit( const BitmapEx& i_rBitmap, bool bDrawMask ) 10085 { 10086 BitmapEx aBitmap( i_rBitmap ); 10087 if( m_aContext.ColorMode == PDFWriter::DrawGreyscale ) 10088 { 10089 BmpConversion eConv = BMP_CONVERSION_8BIT_GREYS; 10090 int nDepth = aBitmap.GetBitmap().GetBitCount(); 10091 if( nDepth <= 4 ) 10092 eConv = BMP_CONVERSION_4BIT_GREYS; 10093 if( nDepth > 1 ) 10094 aBitmap.Convert( eConv ); 10095 } 10096 BitmapID aID; 10097 aID.m_aPixelSize = aBitmap.GetSizePixel(); 10098 aID.m_nSize = aBitmap.GetBitCount(); 10099 aID.m_nChecksum = aBitmap.GetBitmap().GetChecksum(); 10100 aID.m_nMaskChecksum = 0; 10101 if( aBitmap.IsAlpha() ) 10102 aID.m_nMaskChecksum = aBitmap.GetAlpha().GetChecksum(); 10103 else 10104 { 10105 Bitmap aMask = aBitmap.GetMask(); 10106 if( ! aMask.IsEmpty() ) 10107 aID.m_nMaskChecksum = aMask.GetChecksum(); 10108 } 10109 std::list< BitmapEmit >::const_iterator it; 10110 for( it = m_aBitmaps.begin(); it != m_aBitmaps.end(); ++it ) 10111 { 10112 if( aID == it->m_aID ) 10113 break; 10114 } 10115 if( it == m_aBitmaps.end() ) 10116 { 10117 m_aBitmaps.push_front( BitmapEmit() ); 10118 m_aBitmaps.front().m_aID = aID; 10119 m_aBitmaps.front().m_aBitmap = aBitmap; 10120 m_aBitmaps.front().m_nObject = createObject(); 10121 m_aBitmaps.front().m_bDrawMask = bDrawMask; 10122 it = m_aBitmaps.begin(); 10123 } 10124 10125 OStringBuffer aObjName( 16 ); 10126 aObjName.append( "Im" ); 10127 aObjName.append( it->m_nObject ); 10128 pushResource( ResXObject, aObjName.makeStringAndClear(), it->m_nObject ); 10129 10130 return *it; 10131 } 10132 10133 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap ) 10134 { 10135 MARK( "drawBitmap (Bitmap)" ); 10136 10137 // #i40055# sanity check 10138 if( ! (rDestSize.Width() && rDestSize.Height()) ) 10139 return; 10140 10141 const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( rBitmap ) ); 10142 drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) ); 10143 } 10144 10145 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEx& rBitmap ) 10146 { 10147 MARK( "drawBitmap (BitmapEx)" ); 10148 10149 // #i40055# sanity check 10150 if( ! (rDestSize.Width() && rDestSize.Height()) ) 10151 return; 10152 10153 const BitmapEmit& rEmit = createBitmapEmit( rBitmap ); 10154 drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) ); 10155 } 10156 10157 void PDFWriterImpl::drawMask( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap, const Color& rFillColor ) 10158 { 10159 MARK( "drawMask" ); 10160 10161 // #i40055# sanity check 10162 if( ! (rDestSize.Width() && rDestSize.Height()) ) 10163 return; 10164 10165 Bitmap aBitmap( rBitmap ); 10166 if( aBitmap.GetBitCount() > 1 ) 10167 aBitmap.Convert( BMP_CONVERSION_1BIT_THRESHOLD ); 10168 DBG_ASSERT( aBitmap.GetBitCount() == 1, "mask conversion failed" ); 10169 10170 const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( aBitmap ), true ); 10171 drawBitmap( rDestPoint, rDestSize, rEmit, rFillColor ); 10172 } 10173 10174 sal_Int32 PDFWriterImpl::createGradient( const Gradient& rGradient, const Size& rSize ) 10175 { 10176 Size aPtSize( lcl_convert( m_aGraphicsStack.front().m_aMapMode, 10177 MapMode( MAP_POINT ), 10178 getReferenceDevice(), 10179 rSize ) ); 10180 // check if we already have this gradient 10181 std::list<GradientEmit>::iterator it; 10182 // rounding to point will generally lose some pixels 10183 // round up to point boundary 10184 aPtSize.Width()++; 10185 aPtSize.Height()++; 10186 for( it = m_aGradients.begin(); it != m_aGradients.end(); ++it ) 10187 { 10188 if( it->m_aGradient == rGradient ) 10189 { 10190 if( it->m_aSize == aPtSize ) 10191 break; 10192 } 10193 } 10194 if( it == m_aGradients.end() ) 10195 { 10196 m_aGradients.push_front( GradientEmit() ); 10197 m_aGradients.front().m_aGradient = rGradient; 10198 m_aGradients.front().m_nObject = createObject(); 10199 m_aGradients.front().m_aSize = aPtSize; 10200 it = m_aGradients.begin(); 10201 } 10202 10203 OStringBuffer aObjName( 16 ); 10204 aObjName.append( 'P' ); 10205 aObjName.append( it->m_nObject ); 10206 pushResource( ResShading, aObjName.makeStringAndClear(), it->m_nObject ); 10207 10208 return it->m_nObject; 10209 } 10210 10211 void PDFWriterImpl::drawGradient( const Rectangle& rRect, const Gradient& rGradient ) 10212 { 10213 MARK( "drawGradient (Rectangle)" ); 10214 10215 if( m_aContext.Version == PDFWriter::PDF_1_2 ) 10216 { 10217 drawRectangle( rRect ); 10218 return; 10219 } 10220 10221 sal_Int32 nGradient = createGradient( rGradient, rRect.GetSize() ); 10222 10223 Point aTranslate( rRect.BottomLeft() ); 10224 aTranslate += Point( 0, 1 ); 10225 10226 updateGraphicsState(); 10227 10228 OStringBuffer aLine( 80 ); 10229 aLine.append( "q 1 0 0 1 " ); 10230 m_aPages.back().appendPoint( aTranslate, aLine ); 10231 aLine.append( " cm " ); 10232 // if a stroke is appended reset the clip region before stroke 10233 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 10234 aLine.append( "q " ); 10235 aLine.append( "0 0 " ); 10236 m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false ); 10237 aLine.append( ' ' ); 10238 m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine, true ); 10239 aLine.append( " re W n\n" ); 10240 10241 aLine.append( "/P" ); 10242 aLine.append( nGradient ); 10243 aLine.append( " sh " ); 10244 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 10245 { 10246 aLine.append( "Q 0 0 " ); 10247 m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false ); 10248 aLine.append( ' ' ); 10249 m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine, true ); 10250 aLine.append( " re S " ); 10251 } 10252 aLine.append( "Q\n" ); 10253 writeBuffer( aLine.getStr(), aLine.getLength() ); 10254 } 10255 10256 void PDFWriterImpl::drawGradient( const PolyPolygon& rPolyPoly, const Gradient& rGradient ) 10257 { 10258 MARK( "drawGradient (PolyPolygon)" ); 10259 10260 if( m_aContext.Version == PDFWriter::PDF_1_2 ) 10261 { 10262 drawPolyPolygon( rPolyPoly ); 10263 return; 10264 } 10265 10266 Rectangle aBoundRect = rPolyPoly.GetBoundRect(); 10267 sal_Int32 nGradient = createGradient( rGradient, aBoundRect.GetSize() ); 10268 10269 updateGraphicsState(); 10270 10271 Point aTranslate = aBoundRect.BottomLeft(); 10272 int nPolygons = rPolyPoly.Count(); 10273 10274 OStringBuffer aLine( 80*nPolygons ); 10275 aLine.append( "q " ); 10276 // set PolyPolygon as clip path 10277 m_aPages.back().appendPolyPolygon( rPolyPoly, aLine ); 10278 aLine.append( "W* n\n" ); 10279 aLine.append( "1 0 0 1 " ); 10280 m_aPages.back().appendPoint( aTranslate, aLine ); 10281 aLine.append( " cm\n" ); 10282 aLine.append( "/P" ); 10283 aLine.append( nGradient ); 10284 aLine.append( " sh Q\n" ); 10285 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 10286 { 10287 // and draw the surrounding path 10288 m_aPages.back().appendPolyPolygon( rPolyPoly, aLine ); 10289 aLine.append( "S\n" ); 10290 } 10291 writeBuffer( aLine.getStr(), aLine.getLength() ); 10292 } 10293 10294 void PDFWriterImpl::drawHatch( const PolyPolygon& rPolyPoly, const Hatch& rHatch ) 10295 { 10296 MARK( "drawHatch" ); 10297 10298 updateGraphicsState(); 10299 10300 if( rPolyPoly.Count() ) 10301 { 10302 PolyPolygon aPolyPoly( rPolyPoly ); 10303 10304 aPolyPoly.Optimize( POLY_OPTIMIZE_NO_SAME ); 10305 push( PUSH_LINECOLOR ); 10306 setLineColor( rHatch.GetColor() ); 10307 getReferenceDevice()->ImplDrawHatch( aPolyPoly, rHatch, sal_False ); 10308 pop(); 10309 } 10310 } 10311 10312 void PDFWriterImpl::drawWallpaper( const Rectangle& rRect, const Wallpaper& rWall ) 10313 { 10314 MARK( "drawWallpaper" ); 10315 10316 bool bDrawColor = false; 10317 bool bDrawGradient = false; 10318 bool bDrawBitmap = false; 10319 10320 BitmapEx aBitmap; 10321 Point aBmpPos = rRect.TopLeft(); 10322 Size aBmpSize; 10323 if( rWall.IsBitmap() ) 10324 { 10325 aBitmap = rWall.GetBitmap(); 10326 aBmpSize = lcl_convert( aBitmap.GetPrefMapMode(), 10327 getMapMode(), 10328 getReferenceDevice(), 10329 aBitmap.GetPrefSize() ); 10330 Rectangle aRect( rRect ); 10331 if( rWall.IsRect() ) 10332 { 10333 aRect = rWall.GetRect(); 10334 aBmpPos = aRect.TopLeft(); 10335 aBmpSize = aRect.GetSize(); 10336 } 10337 if( rWall.GetStyle() != WALLPAPER_SCALE ) 10338 { 10339 if( rWall.GetStyle() != WALLPAPER_TILE ) 10340 { 10341 bDrawBitmap = true; 10342 if( rWall.IsGradient() ) 10343 bDrawGradient = true; 10344 else 10345 bDrawColor = true; 10346 switch( rWall.GetStyle() ) 10347 { 10348 case WALLPAPER_TOPLEFT: 10349 break; 10350 case WALLPAPER_TOP: 10351 aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2; 10352 break; 10353 case WALLPAPER_LEFT: 10354 aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2; 10355 break; 10356 case WALLPAPER_TOPRIGHT: 10357 aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width(); 10358 break; 10359 case WALLPAPER_CENTER: 10360 aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2; 10361 aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2; 10362 break; 10363 case WALLPAPER_RIGHT: 10364 aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width(); 10365 aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2; 10366 break; 10367 case WALLPAPER_BOTTOMLEFT: 10368 aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height(); 10369 break; 10370 case WALLPAPER_BOTTOM: 10371 aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2; 10372 aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height(); 10373 break; 10374 case WALLPAPER_BOTTOMRIGHT: 10375 aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width(); 10376 aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height(); 10377 break; 10378 default: ; 10379 } 10380 } 10381 else 10382 { 10383 // push the bitmap 10384 const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( aBitmap ) ); 10385 10386 // convert to page coordinates; this needs to be done here 10387 // since the emit does not know the page anymore 10388 Rectangle aConvertRect( aBmpPos, aBmpSize ); 10389 m_aPages.back().convertRect( aConvertRect ); 10390 10391 OStringBuffer aNameBuf(16); 10392 aNameBuf.append( "Im" ); 10393 aNameBuf.append( rEmit.m_nObject ); 10394 OString aImageName( aNameBuf.makeStringAndClear() ); 10395 10396 // push the pattern 10397 OStringBuffer aTilingStream( 32 ); 10398 appendFixedInt( aConvertRect.GetWidth(), aTilingStream ); 10399 aTilingStream.append( " 0 0 " ); 10400 appendFixedInt( aConvertRect.GetHeight(), aTilingStream ); 10401 aTilingStream.append( " 0 0 cm\n/" ); 10402 aTilingStream.append( aImageName ); 10403 aTilingStream.append( " Do\n" ); 10404 10405 m_aTilings.push_back( TilingEmit() ); 10406 m_aTilings.back().m_nObject = createObject(); 10407 m_aTilings.back().m_aRectangle = Rectangle( Point( 0, 0 ), aConvertRect.GetSize() ); 10408 m_aTilings.back().m_pTilingStream = new SvMemoryStream(); 10409 m_aTilings.back().m_pTilingStream->Write( aTilingStream.getStr(), aTilingStream.getLength() ); 10410 // phase the tiling so wallpaper begins on upper left 10411 m_aTilings.back().m_aTransform.matrix[2] = double(aConvertRect.Left() % aConvertRect.GetWidth()) / fDivisor; 10412 m_aTilings.back().m_aTransform.matrix[5] = double(aConvertRect.Top() % aConvertRect.GetHeight()) / fDivisor; 10413 m_aTilings.back().m_aResources.m_aXObjects[aImageName] = rEmit.m_nObject; 10414 10415 updateGraphicsState(); 10416 10417 OStringBuffer aObjName( 16 ); 10418 aObjName.append( 'P' ); 10419 aObjName.append( m_aTilings.back().m_nObject ); 10420 OString aPatternName( aObjName.makeStringAndClear() ); 10421 pushResource( ResPattern, aPatternName, m_aTilings.back().m_nObject ); 10422 10423 // fill a rRect with the pattern 10424 OStringBuffer aLine( 100 ); 10425 aLine.append( "q /Pattern cs /" ); 10426 aLine.append( aPatternName ); 10427 aLine.append( " scn " ); 10428 m_aPages.back().appendRect( rRect, aLine ); 10429 aLine.append( " f Q\n" ); 10430 writeBuffer( aLine.getStr(), aLine.getLength() ); 10431 } 10432 } 10433 else 10434 { 10435 aBmpPos = aRect.TopLeft(); 10436 aBmpSize = aRect.GetSize(); 10437 bDrawBitmap = true; 10438 } 10439 10440 if( aBitmap.IsTransparent() ) 10441 { 10442 if( rWall.IsGradient() ) 10443 bDrawGradient = true; 10444 else 10445 bDrawColor = true; 10446 } 10447 } 10448 else if( rWall.IsGradient() ) 10449 bDrawGradient = true; 10450 else 10451 bDrawColor = true; 10452 10453 if( bDrawGradient ) 10454 { 10455 drawGradient( rRect, rWall.GetGradient() ); 10456 } 10457 if( bDrawColor ) 10458 { 10459 Color aOldLineColor = m_aGraphicsStack.front().m_aLineColor; 10460 Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor; 10461 setLineColor( Color( COL_TRANSPARENT ) ); 10462 setFillColor( rWall.GetColor() ); 10463 drawRectangle( rRect ); 10464 setLineColor( aOldLineColor ); 10465 setFillColor( aOldFillColor ); 10466 } 10467 if( bDrawBitmap ) 10468 { 10469 // set temporary clip region since aBmpPos and aBmpSize 10470 // may be outside rRect 10471 OStringBuffer aLine( 20 ); 10472 aLine.append( "q " ); 10473 m_aPages.back().appendRect( rRect, aLine ); 10474 aLine.append( " W n\n" ); 10475 writeBuffer( aLine.getStr(), aLine.getLength() ); 10476 drawBitmap( aBmpPos, aBmpSize, aBitmap ); 10477 writeBuffer( "Q\n", 2 ); 10478 } 10479 } 10480 10481 void PDFWriterImpl::beginPattern( const Rectangle& rCellRect ) 10482 { 10483 beginRedirect( new SvMemoryStream(), rCellRect ); 10484 } 10485 10486 sal_Int32 PDFWriterImpl::endPattern( const SvtGraphicFill::Transform& rTransform ) 10487 { 10488 Rectangle aConvertRect( getRedirectTargetRect() ); 10489 DBG_ASSERT( aConvertRect.GetWidth() != 0 && aConvertRect.GetHeight() != 0, "empty cell rectangle in pattern" ); 10490 10491 // get scaling between current mapmode and PDF output 10492 Size aScaling( lcl_convert( m_aGraphicsStack.front().m_aMapMode, m_aMapMode, getReferenceDevice(), Size( 10000, 10000 ) ) ); 10493 double fSX = (double(aScaling.Width()) / 10000.0); 10494 double fSY = (double(aScaling.Height()) / 10000.0); 10495 10496 // transform translation part of matrix 10497 Size aTranslation( (long)rTransform.matrix[2], (long)rTransform.matrix[5] ); 10498 aTranslation = lcl_convert( m_aGraphicsStack.front().m_aMapMode, m_aMapMode, getReferenceDevice(), aTranslation ); 10499 10500 sal_Int32 nTilingId = m_aTilings.size(); 10501 m_aTilings.push_back( TilingEmit() ); 10502 TilingEmit& rTile = m_aTilings.back(); 10503 rTile.m_nObject = createObject(); 10504 rTile.m_aResources = m_aOutputStreams.front().m_aResourceDict; 10505 rTile.m_aTransform.matrix[0] = rTransform.matrix[0] * fSX; 10506 rTile.m_aTransform.matrix[1] = rTransform.matrix[1] * fSY; 10507 rTile.m_aTransform.matrix[2] = aTranslation.Width(); 10508 rTile.m_aTransform.matrix[3] = rTransform.matrix[3] * fSX; 10509 rTile.m_aTransform.matrix[4] = rTransform.matrix[4] * fSY; 10510 rTile.m_aTransform.matrix[5] = -aTranslation.Height(); 10511 // caution: endRedirect pops the stream, so do this last 10512 rTile.m_pTilingStream = dynamic_cast<SvMemoryStream*>(endRedirect()); 10513 // FIXME: bound rect will not work with rotated matrix 10514 rTile.m_aRectangle = Rectangle( Point(0,0), aConvertRect.GetSize() ); 10515 rTile.m_aCellSize = aConvertRect.GetSize(); 10516 10517 OStringBuffer aObjName( 16 ); 10518 aObjName.append( 'P' ); 10519 aObjName.append( rTile.m_nObject ); 10520 pushResource( ResPattern, aObjName.makeStringAndClear(), rTile.m_nObject ); 10521 return nTilingId; 10522 } 10523 10524 void PDFWriterImpl::drawPolyPolygon( const PolyPolygon& rPolyPoly, sal_Int32 nPattern, bool bEOFill ) 10525 { 10526 if( nPattern < 0 || nPattern >= (sal_Int32)m_aTilings.size() ) 10527 return; 10528 10529 m_aPages.back().endStream(); 10530 sal_Int32 nXObject = createObject(); 10531 OStringBuffer aNameBuf( 16 ); 10532 aNameBuf.append( "Pol" ); 10533 aNameBuf.append( nXObject ); 10534 OString aObjName( aNameBuf.makeStringAndClear() ); 10535 Rectangle aObjRect; 10536 if( updateObject( nXObject ) ) 10537 { 10538 // get bounding rect of object 10539 PolyPolygon aSubDiv; 10540 rPolyPoly.AdaptiveSubdivide( aSubDiv ); 10541 aObjRect = aSubDiv.GetBoundRect(); 10542 Rectangle aConvObjRect( aObjRect ); 10543 m_aPages.back().convertRect( aConvObjRect ); 10544 10545 // move polypolygon to bottom left of page 10546 PolyPolygon aLocalPath( rPolyPoly ); 10547 sal_Int32 nPgWd = getReferenceDevice()->ImplGetDPIX() * m_aPages.back().getWidth() / 72; 10548 sal_Int32 nPgHt = getReferenceDevice()->ImplGetDPIY() * m_aPages.back().getHeight() / 72; 10549 Size aLogicPgSz = getReferenceDevice()->PixelToLogic( Size( nPgWd, nPgHt ), m_aGraphicsStack.front().m_aMapMode ); 10550 sal_Int32 nXOff = aObjRect.Left(); 10551 sal_Int32 nYOff = aLogicPgSz.Height() - aObjRect.Bottom(); 10552 aLocalPath.Move( -nXOff, nYOff ); 10553 10554 // prepare XObject's content stream 10555 OStringBuffer aStream( 512 ); 10556 aStream.append( "/Pattern cs /P" ); 10557 aStream.append( m_aTilings[ nPattern ].m_nObject ); 10558 aStream.append( " scn\n" ); 10559 m_aPages.back().appendPolyPolygon( aLocalPath, aStream ); 10560 aStream.append( bEOFill ? "f*" : "f" ); 10561 SvMemoryStream aMemStream( aStream.getLength() ); 10562 aMemStream.Write( aStream.getStr(), aStream.getLength() ); 10563 bool bDeflate = compressStream( &aMemStream ); 10564 aMemStream.Seek( STREAM_SEEK_TO_END ); 10565 sal_Int32 nStreamLen = (sal_Int32)aMemStream.Tell(); 10566 aMemStream.Seek( STREAM_SEEK_TO_BEGIN ); 10567 10568 // add new XObject to global resource dict 10569 m_aGlobalResourceDict.m_aXObjects[ aObjName ] = nXObject; 10570 10571 // write XObject 10572 OStringBuffer aLine( 512 ); 10573 aLine.append( nXObject ); 10574 aLine.append( " 0 obj\n" 10575 "<</Type/XObject/Subtype/Form/BBox[0 0 " ); 10576 appendFixedInt( aConvObjRect.GetWidth(), aLine ); 10577 aLine.append( ' ' ); 10578 appendFixedInt( aConvObjRect.GetHeight(), aLine ); 10579 aLine.append( "]/Length " ); 10580 aLine.append( nStreamLen ); 10581 if( bDeflate ) 10582 aLine.append( "/Filter/FlateDecode" ); 10583 aLine.append( ">>\n" 10584 "stream\n" ); 10585 writeBuffer( aLine.getStr(), aLine.getLength() ); 10586 checkAndEnableStreamEncryption( nXObject ); 10587 writeBuffer( aMemStream.GetData(), nStreamLen ); 10588 disableStreamEncryption(); 10589 writeBuffer( "\nendstream\nendobj\n\n", 19 ); 10590 } 10591 m_aPages.back().beginStream(); 10592 OStringBuffer aLine( 80 ); 10593 aLine.append( "q 1 0 0 1 " ); 10594 m_aPages.back().appendPoint( aObjRect.BottomLeft(), aLine ); 10595 aLine.append( " cm/" ); 10596 aLine.append( aObjName ); 10597 aLine.append( " Do Q\n" ); 10598 writeBuffer( aLine.getStr(), aLine.getLength() ); 10599 } 10600 10601 void PDFWriterImpl::updateGraphicsState() 10602 { 10603 OStringBuffer aLine( 256 ); 10604 GraphicsState& rNewState = m_aGraphicsStack.front(); 10605 // first set clip region since it might invalidate everything else 10606 10607 if( (rNewState.m_nUpdateFlags & GraphicsState::updateClipRegion) ) 10608 { 10609 rNewState.m_nUpdateFlags &= ~GraphicsState::updateClipRegion; 10610 10611 if( m_aCurrentPDFState.m_bClipRegion != rNewState.m_bClipRegion || 10612 ( rNewState.m_bClipRegion && m_aCurrentPDFState.m_aClipRegion != rNewState.m_aClipRegion ) ) 10613 { 10614 if( m_aCurrentPDFState.m_bClipRegion && m_aCurrentPDFState.m_aClipRegion.count() ) 10615 { 10616 aLine.append( "Q " ); 10617 // invalidate everything but the clip region 10618 m_aCurrentPDFState = GraphicsState(); 10619 rNewState.m_nUpdateFlags = sal::static_int_cast<sal_uInt16>(~GraphicsState::updateClipRegion); 10620 } 10621 if( rNewState.m_bClipRegion && rNewState.m_aClipRegion.count() ) 10622 { 10623 // clip region is always stored in private PDF mapmode 10624 MapMode aNewMapMode = rNewState.m_aMapMode; 10625 rNewState.m_aMapMode = m_aMapMode; 10626 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode ); 10627 m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode; 10628 10629 aLine.append( "q " ); 10630 m_aPages.back().appendPolyPolygon( rNewState.m_aClipRegion, aLine ); 10631 aLine.append( "W* n\n" ); 10632 rNewState.m_aMapMode = aNewMapMode; 10633 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode ); 10634 m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode; 10635 } 10636 } 10637 } 10638 10639 if( (rNewState.m_nUpdateFlags & GraphicsState::updateMapMode) ) 10640 { 10641 rNewState.m_nUpdateFlags &= ~GraphicsState::updateMapMode; 10642 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode ); 10643 } 10644 10645 if( (rNewState.m_nUpdateFlags & GraphicsState::updateFont) ) 10646 { 10647 rNewState.m_nUpdateFlags &= ~GraphicsState::updateFont; 10648 getReferenceDevice()->SetFont( rNewState.m_aFont ); 10649 getReferenceDevice()->ImplNewFont(); 10650 } 10651 10652 if( (rNewState.m_nUpdateFlags & GraphicsState::updateLayoutMode) ) 10653 { 10654 rNewState.m_nUpdateFlags &= ~GraphicsState::updateLayoutMode; 10655 getReferenceDevice()->SetLayoutMode( rNewState.m_nLayoutMode ); 10656 } 10657 10658 if( (rNewState.m_nUpdateFlags & GraphicsState::updateDigitLanguage) ) 10659 { 10660 rNewState.m_nUpdateFlags &= ~GraphicsState::updateDigitLanguage; 10661 getReferenceDevice()->SetDigitLanguage( rNewState.m_aDigitLanguage ); 10662 } 10663 10664 if( (rNewState.m_nUpdateFlags & GraphicsState::updateLineColor) ) 10665 { 10666 rNewState.m_nUpdateFlags &= ~GraphicsState::updateLineColor; 10667 if( m_aCurrentPDFState.m_aLineColor != rNewState.m_aLineColor && 10668 rNewState.m_aLineColor != Color( COL_TRANSPARENT ) ) 10669 { 10670 appendStrokingColor( rNewState.m_aLineColor, aLine ); 10671 aLine.append( "\n" ); 10672 } 10673 } 10674 10675 if( (rNewState.m_nUpdateFlags & GraphicsState::updateFillColor) ) 10676 { 10677 rNewState.m_nUpdateFlags &= ~GraphicsState::updateFillColor; 10678 if( m_aCurrentPDFState.m_aFillColor != rNewState.m_aFillColor && 10679 rNewState.m_aFillColor != Color( COL_TRANSPARENT ) ) 10680 { 10681 appendNonStrokingColor( rNewState.m_aFillColor, aLine ); 10682 aLine.append( "\n" ); 10683 } 10684 } 10685 10686 if( (rNewState.m_nUpdateFlags & GraphicsState::updateTransparentPercent) ) 10687 { 10688 rNewState.m_nUpdateFlags &= ~GraphicsState::updateTransparentPercent; 10689 if( m_aContext.Version >= PDFWriter::PDF_1_4 && m_aCurrentPDFState.m_nTransparentPercent != rNewState.m_nTransparentPercent ) 10690 { 10691 // TODO: switch extended graphicsstate 10692 } 10693 } 10694 10695 // everything is up to date now 10696 m_aCurrentPDFState = m_aGraphicsStack.front(); 10697 if( aLine.getLength() ) 10698 writeBuffer( aLine.getStr(), aLine.getLength() ); 10699 } 10700 10701 /* #i47544# imitate OutputDevice behaviour: 10702 * if a font with a nontransparent color is set, it overwrites the current 10703 * text color. OTOH setting the text color will overwrite the color of the font. 10704 */ 10705 void PDFWriterImpl::setFont( const Font& rFont ) 10706 { 10707 Color aColor = rFont.GetColor(); 10708 if( aColor == Color( COL_TRANSPARENT ) ) 10709 aColor = m_aGraphicsStack.front().m_aFont.GetColor(); 10710 m_aGraphicsStack.front().m_aFont = rFont; 10711 m_aGraphicsStack.front().m_aFont.SetColor( aColor ); 10712 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateFont; 10713 } 10714 10715 void PDFWriterImpl::push( sal_uInt16 nFlags ) 10716 { 10717 OSL_ENSURE( m_aGraphicsStack.size() > 0, "invalid graphics stack" ); 10718 m_aGraphicsStack.push_front( m_aGraphicsStack.front() ); 10719 m_aGraphicsStack.front().m_nFlags = nFlags; 10720 } 10721 10722 void PDFWriterImpl::pop() 10723 { 10724 OSL_ENSURE( m_aGraphicsStack.size() > 1, "pop without push" ); 10725 if( m_aGraphicsStack.size() < 2 ) 10726 return; 10727 10728 GraphicsState aState = m_aGraphicsStack.front(); 10729 m_aGraphicsStack.pop_front(); 10730 GraphicsState& rOld = m_aGraphicsStack.front(); 10731 10732 // move those parameters back that were not pushed 10733 // in the first place 10734 if( ! (aState.m_nFlags & PUSH_LINECOLOR) ) 10735 setLineColor( aState.m_aLineColor ); 10736 if( ! (aState.m_nFlags & PUSH_FILLCOLOR) ) 10737 setFillColor( aState.m_aFillColor ); 10738 if( ! (aState.m_nFlags & PUSH_FONT) ) 10739 setFont( aState.m_aFont ); 10740 if( ! (aState.m_nFlags & PUSH_TEXTCOLOR) ) 10741 setTextColor( aState.m_aFont.GetColor() ); 10742 if( ! (aState.m_nFlags & PUSH_MAPMODE) ) 10743 setMapMode( aState.m_aMapMode ); 10744 if( ! (aState.m_nFlags & PUSH_CLIPREGION) ) 10745 { 10746 // do not use setClipRegion here 10747 // it would convert again assuming the current mapmode 10748 rOld.m_aClipRegion = aState.m_aClipRegion; 10749 rOld.m_bClipRegion = aState.m_bClipRegion; 10750 } 10751 if( ! (aState.m_nFlags & PUSH_TEXTLINECOLOR ) ) 10752 setTextLineColor( aState.m_aTextLineColor ); 10753 if( ! (aState.m_nFlags & PUSH_OVERLINECOLOR ) ) 10754 setOverlineColor( aState.m_aOverlineColor ); 10755 if( ! (aState.m_nFlags & PUSH_TEXTALIGN ) ) 10756 setTextAlign( aState.m_aFont.GetAlign() ); 10757 if( ! (aState.m_nFlags & PUSH_TEXTFILLCOLOR) ) 10758 setTextFillColor( aState.m_aFont.GetFillColor() ); 10759 if( ! (aState.m_nFlags & PUSH_REFPOINT) ) 10760 { 10761 // what ? 10762 } 10763 // invalidate graphics state 10764 m_aGraphicsStack.front().m_nUpdateFlags = sal::static_int_cast<sal_uInt16>(~0U); 10765 } 10766 10767 void PDFWriterImpl::setMapMode( const MapMode& rMapMode ) 10768 { 10769 m_aGraphicsStack.front().m_aMapMode = rMapMode; 10770 getReferenceDevice()->SetMapMode( rMapMode ); 10771 m_aCurrentPDFState.m_aMapMode = rMapMode; 10772 } 10773 10774 void PDFWriterImpl::setClipRegion( const basegfx::B2DPolyPolygon& rRegion ) 10775 { 10776 basegfx::B2DPolyPolygon aRegion = getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode ); 10777 aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode ); 10778 m_aGraphicsStack.front().m_aClipRegion = aRegion; 10779 m_aGraphicsStack.front().m_bClipRegion = true; 10780 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion; 10781 } 10782 10783 void PDFWriterImpl::moveClipRegion( sal_Int32 nX, sal_Int32 nY ) 10784 { 10785 if( m_aGraphicsStack.front().m_bClipRegion && m_aGraphicsStack.front().m_aClipRegion.count() ) 10786 { 10787 Point aPoint( lcl_convert( m_aGraphicsStack.front().m_aMapMode, 10788 m_aMapMode, 10789 getReferenceDevice(), 10790 Point( nX, nY ) ) ); 10791 aPoint -= lcl_convert( m_aGraphicsStack.front().m_aMapMode, 10792 m_aMapMode, 10793 getReferenceDevice(), 10794 Point() ); 10795 basegfx::B2DHomMatrix aMat; 10796 aMat.translate( aPoint.X(), aPoint.Y() ); 10797 m_aGraphicsStack.front().m_aClipRegion.transform( aMat ); 10798 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion; 10799 } 10800 } 10801 10802 bool PDFWriterImpl::intersectClipRegion( const Rectangle& rRect ) 10803 { 10804 basegfx::B2DPolyPolygon aRect( basegfx::tools::createPolygonFromRect( 10805 basegfx::B2DRectangle( rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom() ) ) ); 10806 return intersectClipRegion( aRect ); 10807 } 10808 10809 10810 bool PDFWriterImpl::intersectClipRegion( const basegfx::B2DPolyPolygon& rRegion ) 10811 { 10812 basegfx::B2DPolyPolygon aRegion( getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode ) ); 10813 aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode ); 10814 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion; 10815 if( m_aGraphicsStack.front().m_bClipRegion ) 10816 { 10817 basegfx::B2DPolyPolygon aOld( basegfx::tools::prepareForPolygonOperation( m_aGraphicsStack.front().m_aClipRegion ) ); 10818 aRegion = basegfx::tools::prepareForPolygonOperation( aRegion ); 10819 m_aGraphicsStack.front().m_aClipRegion = basegfx::tools::solvePolygonOperationAnd( aOld, aRegion ); 10820 } 10821 else 10822 { 10823 m_aGraphicsStack.front().m_aClipRegion = aRegion; 10824 m_aGraphicsStack.front().m_bClipRegion = true; 10825 } 10826 return true; 10827 } 10828 10829 void PDFWriterImpl::createNote( const Rectangle& rRect, const PDFNote& rNote, sal_Int32 nPageNr ) 10830 { 10831 if( nPageNr < 0 ) 10832 nPageNr = m_nCurrentPage; 10833 10834 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 10835 return; 10836 10837 m_aNotes.push_back( PDFNoteEntry() ); 10838 m_aNotes.back().m_nObject = createObject(); 10839 m_aNotes.back().m_aContents = rNote; 10840 m_aNotes.back().m_aRect = rRect; 10841 // convert to default user space now, since the mapmode may change 10842 m_aPages[nPageNr].convertRect( m_aNotes.back().m_aRect ); 10843 10844 // insert note to page's annotation list 10845 m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aNotes.back().m_nObject ); 10846 } 10847 10848 sal_Int32 PDFWriterImpl::createLink( const Rectangle& rRect, sal_Int32 nPageNr ) 10849 { 10850 if( nPageNr < 0 ) 10851 nPageNr = m_nCurrentPage; 10852 10853 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 10854 return -1; 10855 10856 sal_Int32 nRet = m_aLinks.size(); 10857 10858 m_aLinks.push_back( PDFLink() ); 10859 m_aLinks.back().m_nObject = createObject(); 10860 m_aLinks.back().m_nPage = nPageNr; 10861 m_aLinks.back().m_aRect = rRect; 10862 // convert to default user space now, since the mapmode may change 10863 m_aPages[nPageNr].convertRect( m_aLinks.back().m_aRect ); 10864 10865 // insert link to page's annotation list 10866 m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aLinks.back().m_nObject ); 10867 10868 return nRet; 10869 } 10870 10871 //--->i56629 10872 sal_Int32 PDFWriterImpl::createNamedDest( const rtl::OUString& sDestName, const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType ) 10873 { 10874 if( nPageNr < 0 ) 10875 nPageNr = m_nCurrentPage; 10876 10877 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 10878 return -1; 10879 10880 sal_Int32 nRet = m_aNamedDests.size(); 10881 10882 m_aNamedDests.push_back( PDFNamedDest() ); 10883 m_aNamedDests.back().m_aDestName = sDestName; 10884 m_aNamedDests.back().m_nPage = nPageNr; 10885 m_aNamedDests.back().m_eType = eType; 10886 m_aNamedDests.back().m_aRect = rRect; 10887 // convert to default user space now, since the mapmode may change 10888 m_aPages[nPageNr].convertRect( m_aNamedDests.back().m_aRect ); 10889 10890 return nRet; 10891 } 10892 //<---i56629 10893 10894 sal_Int32 PDFWriterImpl::createDest( const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType ) 10895 { 10896 if( nPageNr < 0 ) 10897 nPageNr = m_nCurrentPage; 10898 10899 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 10900 return -1; 10901 10902 sal_Int32 nRet = m_aDests.size(); 10903 10904 m_aDests.push_back( PDFDest() ); 10905 m_aDests.back().m_nPage = nPageNr; 10906 m_aDests.back().m_eType = eType; 10907 m_aDests.back().m_aRect = rRect; 10908 // convert to default user space now, since the mapmode may change 10909 m_aPages[nPageNr].convertRect( m_aDests.back().m_aRect ); 10910 10911 return nRet; 10912 } 10913 10914 sal_Int32 PDFWriterImpl::registerDestReference( sal_Int32 nDestId, const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType ) 10915 { 10916 return m_aDestinationIdTranslation[ nDestId ] = createDest( rRect, nPageNr, eType ); 10917 } 10918 10919 sal_Int32 PDFWriterImpl::setLinkDest( sal_Int32 nLinkId, sal_Int32 nDestId ) 10920 { 10921 if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() ) 10922 return -1; 10923 if( nDestId < 0 || nDestId >= (sal_Int32)m_aDests.size() ) 10924 return -2; 10925 10926 m_aLinks[ nLinkId ].m_nDest = nDestId; 10927 10928 return 0; 10929 } 10930 10931 sal_Int32 PDFWriterImpl::setLinkURL( sal_Int32 nLinkId, const OUString& rURL ) 10932 { 10933 if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() ) 10934 return -1; 10935 10936 m_aLinks[ nLinkId ].m_nDest = -1; 10937 10938 using namespace ::com::sun::star; 10939 10940 if (!m_xTrans.is()) 10941 { 10942 uno::Reference< lang::XMultiServiceFactory > xFact( comphelper::getProcessServiceFactory() ); 10943 if( xFact.is() ) 10944 { 10945 m_xTrans = uno::Reference < util::XURLTransformer >( 10946 xFact->createInstance( OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.util.URLTransformer" ) ) ), uno::UNO_QUERY ); 10947 } 10948 } 10949 10950 util::URL aURL; 10951 aURL.Complete = rURL; 10952 10953 if (m_xTrans.is()) 10954 m_xTrans->parseStrict( aURL ); 10955 10956 m_aLinks[ nLinkId ].m_aURL = aURL.Complete; 10957 10958 return 0; 10959 } 10960 10961 void PDFWriterImpl::setLinkPropertyId( sal_Int32 nLinkId, sal_Int32 nPropertyId ) 10962 { 10963 m_aLinkPropertyMap[ nPropertyId ] = nLinkId; 10964 } 10965 10966 sal_Int32 PDFWriterImpl::createOutlineItem( sal_Int32 nParent, const OUString& rText, sal_Int32 nDestID ) 10967 { 10968 // create new item 10969 sal_Int32 nNewItem = m_aOutline.size(); 10970 m_aOutline.push_back( PDFOutlineEntry() ); 10971 10972 // set item attributes 10973 setOutlineItemParent( nNewItem, nParent ); 10974 setOutlineItemText( nNewItem, rText ); 10975 setOutlineItemDest( nNewItem, nDestID ); 10976 10977 return nNewItem; 10978 } 10979 10980 sal_Int32 PDFWriterImpl::setOutlineItemParent( sal_Int32 nItem, sal_Int32 nNewParent ) 10981 { 10982 if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() ) 10983 return -1; 10984 10985 int nRet = 0; 10986 10987 if( nNewParent < 0 || nNewParent >= (sal_Int32)m_aOutline.size() || nNewParent == nItem ) 10988 { 10989 nNewParent = 0; 10990 nRet = -2; 10991 } 10992 // remove item from previous parent 10993 sal_Int32 nParentID = m_aOutline[ nItem ].m_nParentID; 10994 if( nParentID >= 0 && nParentID < (sal_Int32)m_aOutline.size() ) 10995 { 10996 PDFOutlineEntry& rParent = m_aOutline[ nParentID ]; 10997 10998 for( std::vector<sal_Int32>::iterator it = rParent.m_aChildren.begin(); 10999 it != rParent.m_aChildren.end(); ++it ) 11000 { 11001 if( *it == nItem ) 11002 { 11003 rParent.m_aChildren.erase( it ); 11004 break; 11005 } 11006 } 11007 } 11008 11009 // insert item to new parent's list of children 11010 m_aOutline[ nNewParent ].m_aChildren.push_back( nItem ); 11011 11012 return nRet; 11013 } 11014 11015 sal_Int32 PDFWriterImpl::setOutlineItemText( sal_Int32 nItem, const OUString& rText ) 11016 { 11017 if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() ) 11018 return -1; 11019 11020 m_aOutline[ nItem ].m_aTitle = psp::WhitespaceToSpace( rText ); 11021 return 0; 11022 } 11023 11024 sal_Int32 PDFWriterImpl::setOutlineItemDest( sal_Int32 nItem, sal_Int32 nDestID ) 11025 { 11026 if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() ) // item does not exist 11027 return -1; 11028 if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() ) // dest does not exist 11029 return -2; 11030 m_aOutline[nItem].m_nDestID = nDestID; 11031 return 0; 11032 } 11033 11034 const sal_Char* PDFWriterImpl::getStructureTag( PDFWriter::StructElement eType ) 11035 { 11036 static std::map< PDFWriter::StructElement, const char* > aTagStrings; 11037 if( aTagStrings.empty() ) 11038 { 11039 aTagStrings[ PDFWriter::NonStructElement] = "NonStruct"; 11040 aTagStrings[ PDFWriter::Document ] = "Document"; 11041 aTagStrings[ PDFWriter::Part ] = "Part"; 11042 aTagStrings[ PDFWriter::Article ] = "Art"; 11043 aTagStrings[ PDFWriter::Section ] = "Sect"; 11044 aTagStrings[ PDFWriter::Division ] = "Div"; 11045 aTagStrings[ PDFWriter::BlockQuote ] = "BlockQuote"; 11046 aTagStrings[ PDFWriter::Caption ] = "Caption"; 11047 aTagStrings[ PDFWriter::TOC ] = "TOC"; 11048 aTagStrings[ PDFWriter::TOCI ] = "TOCI"; 11049 aTagStrings[ PDFWriter::Index ] = "Index"; 11050 aTagStrings[ PDFWriter::Paragraph ] = "P"; 11051 aTagStrings[ PDFWriter::Heading ] = "H"; 11052 aTagStrings[ PDFWriter::H1 ] = "H1"; 11053 aTagStrings[ PDFWriter::H2 ] = "H2"; 11054 aTagStrings[ PDFWriter::H3 ] = "H3"; 11055 aTagStrings[ PDFWriter::H4 ] = "H4"; 11056 aTagStrings[ PDFWriter::H5 ] = "H5"; 11057 aTagStrings[ PDFWriter::H6 ] = "H6"; 11058 aTagStrings[ PDFWriter::List ] = "L"; 11059 aTagStrings[ PDFWriter::ListItem ] = "LI"; 11060 aTagStrings[ PDFWriter::LILabel ] = "Lbl"; 11061 aTagStrings[ PDFWriter::LIBody ] = "LBody"; 11062 aTagStrings[ PDFWriter::Table ] = "Table"; 11063 aTagStrings[ PDFWriter::TableRow ] = "TR"; 11064 aTagStrings[ PDFWriter::TableHeader ] = "TH"; 11065 aTagStrings[ PDFWriter::TableData ] = "TD"; 11066 aTagStrings[ PDFWriter::Span ] = "Span"; 11067 aTagStrings[ PDFWriter::Quote ] = "Quote"; 11068 aTagStrings[ PDFWriter::Note ] = "Note"; 11069 aTagStrings[ PDFWriter::Reference ] = "Reference"; 11070 aTagStrings[ PDFWriter::BibEntry ] = "BibEntry"; 11071 aTagStrings[ PDFWriter::Code ] = "Code"; 11072 aTagStrings[ PDFWriter::Link ] = "Link"; 11073 aTagStrings[ PDFWriter::Figure ] = "Figure"; 11074 aTagStrings[ PDFWriter::Formula ] = "Formula"; 11075 aTagStrings[ PDFWriter::Form ] = "Form"; 11076 } 11077 11078 std::map< PDFWriter::StructElement, const char* >::const_iterator it = aTagStrings.find( eType ); 11079 11080 return it != aTagStrings.end() ? it->second : "Div"; 11081 } 11082 11083 void PDFWriterImpl::beginStructureElementMCSeq() 11084 { 11085 if( m_bEmitStructure && 11086 m_nCurrentStructElement > 0 && // StructTreeRoot 11087 ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence 11088 ) 11089 { 11090 PDFStructureElement& rEle = m_aStructure[ m_nCurrentStructElement ]; 11091 OStringBuffer aLine( 128 ); 11092 sal_Int32 nMCID = m_aPages[ m_nCurrentPage ].m_aMCIDParents.size(); 11093 aLine.append( "/" ); 11094 if( rEle.m_aAlias.getLength() > 0 ) 11095 aLine.append( rEle.m_aAlias ); 11096 else 11097 aLine.append( getStructureTag( rEle.m_eType ) ); 11098 aLine.append( "<</MCID " ); 11099 aLine.append( nMCID ); 11100 aLine.append( ">>BDC\n" ); 11101 writeBuffer( aLine.getStr(), aLine.getLength() ); 11102 11103 // update the element's content list 11104 #if OSL_DEBUG_LEVEL > 1 11105 fprintf( stderr, "beginning marked content id %" SAL_PRIdINT32 " on page object %" SAL_PRIdINT32 ", structure first page = %" SAL_PRIdINT32 "\n", 11106 nMCID, 11107 m_aPages[ m_nCurrentPage ].m_nPageObject, 11108 rEle.m_nFirstPageObject ); 11109 #endif 11110 rEle.m_aKids.push_back( PDFStructureElementKid( nMCID, m_aPages[m_nCurrentPage].m_nPageObject ) ); 11111 // update the page's mcid parent list 11112 m_aPages[ m_nCurrentPage ].m_aMCIDParents.push_back( rEle.m_nObject ); 11113 // mark element MC sequence as open 11114 rEle.m_bOpenMCSeq = true; 11115 } 11116 // handle artifacts 11117 else if( ! m_bEmitStructure && m_aContext.Tagged && 11118 m_nCurrentStructElement > 0 && 11119 m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement && 11120 ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence 11121 ) 11122 { 11123 OStringBuffer aLine( 128 ); 11124 aLine.append( "/Artifact BMC\n" ); 11125 writeBuffer( aLine.getStr(), aLine.getLength() ); 11126 // mark element MC sequence as open 11127 m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = true; 11128 } 11129 } 11130 11131 void PDFWriterImpl::endStructureElementMCSeq() 11132 { 11133 if( m_nCurrentStructElement > 0 && // StructTreeRoot 11134 ( m_bEmitStructure || m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement ) && 11135 m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // must have an opened MC sequence 11136 ) 11137 { 11138 writeBuffer( "EMC\n", 4 ); 11139 m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = false; 11140 } 11141 } 11142 11143 bool PDFWriterImpl::checkEmitStructure() 11144 { 11145 bool bEmit = false; 11146 if( m_aContext.Tagged ) 11147 { 11148 bEmit = true; 11149 sal_Int32 nEle = m_nCurrentStructElement; 11150 while( nEle > 0 && nEle < sal_Int32(m_aStructure.size()) ) 11151 { 11152 if( m_aStructure[ nEle ].m_eType == PDFWriter::NonStructElement ) 11153 { 11154 bEmit = false; 11155 break; 11156 } 11157 nEle = m_aStructure[ nEle ].m_nParentElement; 11158 } 11159 } 11160 return bEmit; 11161 } 11162 11163 sal_Int32 PDFWriterImpl::beginStructureElement( PDFWriter::StructElement eType, const rtl::OUString& rAlias ) 11164 { 11165 if( m_nCurrentPage < 0 ) 11166 return -1; 11167 11168 if( ! m_aContext.Tagged ) 11169 return -1; 11170 11171 // close eventual current MC sequence 11172 endStructureElementMCSeq(); 11173 11174 if( m_nCurrentStructElement == 0 && 11175 eType != PDFWriter::Document && eType != PDFWriter::NonStructElement ) 11176 { 11177 // struct tree root hit, but not beginning document 11178 // this might happen with setCurrentStructureElement 11179 // silently insert structure into document again if one properly exists 11180 if( ! m_aStructure[ 0 ].m_aChildren.empty() ) 11181 { 11182 PDFWriter::StructElement childType = PDFWriter::NonStructElement; 11183 sal_Int32 nNewCurElement = 0; 11184 const std::list< sal_Int32 >& rRootChildren = m_aStructure[0].m_aChildren; 11185 for( std::list< sal_Int32 >::const_iterator it = rRootChildren.begin(); 11186 childType != PDFWriter::Document && it != rRootChildren.end(); ++it ) 11187 { 11188 nNewCurElement = *it; 11189 childType = m_aStructure[ nNewCurElement ].m_eType; 11190 } 11191 if( childType == PDFWriter::Document ) 11192 { 11193 m_nCurrentStructElement = nNewCurElement; 11194 DBG_ASSERT( 0, "Structure element inserted to StructTreeRoot that is not a document" ); 11195 } 11196 else { 11197 DBG_ERROR( "document structure in disorder !" ); 11198 } 11199 } 11200 else { 11201 DBG_ERROR( "PDF document structure MUST be contained in a Document element" ); 11202 } 11203 } 11204 11205 sal_Int32 nNewId = sal_Int32(m_aStructure.size()); 11206 m_aStructure.push_back( PDFStructureElement() ); 11207 PDFStructureElement& rEle = m_aStructure.back(); 11208 rEle.m_eType = eType; 11209 rEle.m_nOwnElement = nNewId; 11210 rEle.m_nParentElement = m_nCurrentStructElement; 11211 rEle.m_nFirstPageObject = m_aPages[ m_nCurrentPage ].m_nPageObject; 11212 m_aStructure[ m_nCurrentStructElement ].m_aChildren.push_back( nNewId ); 11213 m_nCurrentStructElement = nNewId; 11214 11215 // handle alias names 11216 if( rAlias.getLength() && eType != PDFWriter::NonStructElement ) 11217 { 11218 OStringBuffer aNameBuf( rAlias.getLength() ); 11219 appendName( rAlias, aNameBuf ); 11220 OString aAliasName( aNameBuf.makeStringAndClear() ); 11221 rEle.m_aAlias = aAliasName; 11222 m_aRoleMap[ aAliasName ] = getStructureTag( eType ); 11223 } 11224 11225 #if OSL_DEBUG_LEVEL > 1 11226 OStringBuffer aLine( "beginStructureElement " ); 11227 aLine.append( m_nCurrentStructElement ); 11228 aLine.append( ": " ); 11229 aLine.append( getStructureTag( eType ) ); 11230 if( rEle.m_aAlias.getLength() ) 11231 { 11232 aLine.append( " aliased as \"" ); 11233 aLine.append( rEle.m_aAlias ); 11234 aLine.append( '\"' ); 11235 } 11236 emitComment( aLine.getStr() ); 11237 #endif 11238 11239 // check whether to emit structure henceforth 11240 m_bEmitStructure = checkEmitStructure(); 11241 11242 if( m_bEmitStructure ) // don't create nonexistant objects 11243 { 11244 rEle.m_nObject = createObject(); 11245 // update parent's kids list 11246 m_aStructure[ rEle.m_nParentElement ].m_aKids.push_back( rEle.m_nObject ); 11247 } 11248 return nNewId; 11249 } 11250 11251 void PDFWriterImpl::endStructureElement() 11252 { 11253 if( m_nCurrentPage < 0 ) 11254 return; 11255 11256 if( ! m_aContext.Tagged ) 11257 return; 11258 11259 if( m_nCurrentStructElement == 0 ) 11260 { 11261 // hit the struct tree root, that means there is an endStructureElement 11262 // without corresponding beginStructureElement 11263 return; 11264 } 11265 11266 // end the marked content sequence 11267 endStructureElementMCSeq(); 11268 11269 #if OSL_DEBUG_LEVEL > 1 11270 OStringBuffer aLine( "endStructureElement " ); 11271 aLine.append( m_nCurrentStructElement ); 11272 aLine.append( ": " ); 11273 aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) ); 11274 if( m_aStructure[ m_nCurrentStructElement ].m_aAlias.getLength() ) 11275 { 11276 aLine.append( " aliased as \"" ); 11277 aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias ); 11278 aLine.append( '\"' ); 11279 } 11280 #endif 11281 11282 // "end" the structure element, the parent becomes current element 11283 m_nCurrentStructElement = m_aStructure[ m_nCurrentStructElement ].m_nParentElement; 11284 11285 // check whether to emit structure henceforth 11286 m_bEmitStructure = checkEmitStructure(); 11287 11288 #if OSL_DEBUG_LEVEL > 1 11289 if( m_bEmitStructure ) 11290 emitComment( aLine.getStr() ); 11291 #endif 11292 } 11293 11294 //---> i94258 11295 /* 11296 * This function adds an internal structure list container to overcome the 8191 elements array limitation 11297 * in kids element emission. 11298 * Recursive function 11299 * 11300 */ 11301 void PDFWriterImpl::addInternalStructureContainer( PDFStructureElement& rEle ) 11302 { 11303 if( rEle.m_eType == PDFWriter::NonStructElement && 11304 rEle.m_nOwnElement != rEle.m_nParentElement ) 11305 return; 11306 11307 for( std::list< sal_Int32 >::const_iterator it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); ++it ) 11308 { 11309 if( *it > 0 && *it < sal_Int32(m_aStructure.size()) ) 11310 { 11311 PDFStructureElement& rChild = m_aStructure[ *it ]; 11312 if( rChild.m_eType != PDFWriter::NonStructElement ) 11313 { 11314 //triggered when a child of the rEle element is found 11315 if( rChild.m_nParentElement == rEle.m_nOwnElement ) 11316 addInternalStructureContainer( rChild );//examine the child 11317 else 11318 { 11319 DBG_ERROR( "PDFWriterImpl::addInternalStructureContainer: invalid child structure element" ); 11320 #if OSL_DEBUG_LEVEL > 1 11321 fprintf( stderr, "PDFWriterImpl::addInternalStructureContainer: invalid child structure elemnt with id %" SAL_PRIdINT32 "\n", *it ); 11322 #endif 11323 } 11324 } 11325 } 11326 else 11327 { 11328 DBG_ERROR( "PDFWriterImpl::emitStructure: invalid child structure id" ); 11329 #if OSL_DEBUG_LEVEL > 1 11330 fprintf( stderr, "PDFWriterImpl::addInternalStructureContainer: invalid child structure id %" SAL_PRIdINT32 "\n", *it ); 11331 #endif 11332 } 11333 } 11334 11335 if( rEle.m_nOwnElement != rEle.m_nParentElement ) 11336 { 11337 if( !rEle.m_aKids.empty() ) 11338 { 11339 if( rEle.m_aKids.size() > ncMaxPDFArraySize ) { 11340 //then we need to add the containers for the kids elements 11341 // a list to be used for the new kid element 11342 std::list< PDFStructureElementKid > aNewKids; 11343 std::list< sal_Int32 > aNewChildren; 11344 11345 // add Div in RoleMap, in case no one else did (TODO: is it needed? Is it dangerous?) 11346 OStringBuffer aNameBuf( "Div" ); 11347 OString aAliasName( aNameBuf.makeStringAndClear() ); 11348 m_aRoleMap[ aAliasName ] = getStructureTag( PDFWriter::Division ); 11349 11350 while( rEle.m_aKids.size() > ncMaxPDFArraySize ) 11351 { 11352 sal_Int32 nCurrentStructElement = rEle.m_nOwnElement; 11353 sal_Int32 nNewId = sal_Int32(m_aStructure.size()); 11354 m_aStructure.push_back( PDFStructureElement() ); 11355 PDFStructureElement& rEleNew = m_aStructure.back(); 11356 rEleNew.m_aAlias = aAliasName; 11357 rEleNew.m_eType = PDFWriter::Division; // a new Div type container 11358 rEleNew.m_nOwnElement = nNewId; 11359 rEleNew.m_nParentElement = nCurrentStructElement; 11360 //inherit the same page as the first child to be reparented 11361 rEleNew.m_nFirstPageObject = m_aStructure[ rEle.m_aChildren.front() ].m_nFirstPageObject; 11362 rEleNew.m_nObject = createObject();//assign a PDF object number 11363 //add the object to the kid list of the parent 11364 aNewKids.push_back( PDFStructureElementKid( rEleNew.m_nObject ) ); 11365 aNewChildren.push_back( nNewId ); 11366 11367 std::list< sal_Int32 >::iterator aChildEndIt( rEle.m_aChildren.begin() ); 11368 std::list< PDFStructureElementKid >::iterator aKidEndIt( rEle.m_aKids.begin() ); 11369 advance( aChildEndIt, ncMaxPDFArraySize ); 11370 advance( aKidEndIt, ncMaxPDFArraySize ); 11371 11372 rEleNew.m_aKids.splice( rEleNew.m_aKids.begin(), 11373 rEle.m_aKids, 11374 rEle.m_aKids.begin(), 11375 aKidEndIt ); 11376 rEleNew.m_aChildren.splice( rEleNew.m_aChildren.begin(), 11377 rEle.m_aChildren, 11378 rEle.m_aChildren.begin(), 11379 aChildEndIt ); 11380 // set the kid's new parent 11381 for( std::list< sal_Int32 >::const_iterator it = rEleNew.m_aChildren.begin(); 11382 it != rEleNew.m_aChildren.end(); ++it ) 11383 { 11384 m_aStructure[ *it ].m_nParentElement = nNewId; 11385 } 11386 } 11387 //finally add the new kids resulting from the container added 11388 rEle.m_aKids.insert( rEle.m_aKids.begin(), aNewKids.begin(), aNewKids.end() ); 11389 rEle.m_aChildren.insert( rEle.m_aChildren.begin(), aNewChildren.begin(), aNewChildren.end() ); 11390 } 11391 } 11392 } 11393 } 11394 //<--- i94258 11395 11396 bool PDFWriterImpl::setCurrentStructureElement( sal_Int32 nEle ) 11397 { 11398 bool bSuccess = false; 11399 11400 if( m_aContext.Tagged && nEle >= 0 && nEle < sal_Int32(m_aStructure.size()) ) 11401 { 11402 // end eventual previous marked content sequence 11403 endStructureElementMCSeq(); 11404 11405 m_nCurrentStructElement = nEle; 11406 m_bEmitStructure = checkEmitStructure(); 11407 #if OSL_DEBUG_LEVEL > 1 11408 OStringBuffer aLine( "setCurrentStructureElement " ); 11409 aLine.append( m_nCurrentStructElement ); 11410 aLine.append( ": " ); 11411 aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) ); 11412 if( m_aStructure[ m_nCurrentStructElement ].m_aAlias.getLength() ) 11413 { 11414 aLine.append( " aliased as \"" ); 11415 aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias ); 11416 aLine.append( '\"' ); 11417 } 11418 if( ! m_bEmitStructure ) 11419 aLine.append( " (inside NonStruct)" ); 11420 emitComment( aLine.getStr() ); 11421 #endif 11422 bSuccess = true; 11423 } 11424 11425 return bSuccess; 11426 } 11427 11428 sal_Int32 PDFWriterImpl::getCurrentStructureElement() 11429 { 11430 return m_nCurrentStructElement; 11431 } 11432 11433 bool PDFWriterImpl::setStructureAttribute( enum PDFWriter::StructAttribute eAttr, enum PDFWriter::StructAttributeValue eVal ) 11434 { 11435 if( !m_aContext.Tagged ) 11436 return false; 11437 11438 bool bInsert = false; 11439 if( m_nCurrentStructElement > 0 && m_bEmitStructure ) 11440 { 11441 PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType; 11442 switch( eAttr ) 11443 { 11444 case PDFWriter::Placement: 11445 if( eVal == PDFWriter::Block || 11446 eVal == PDFWriter::Inline || 11447 eVal == PDFWriter::Before || 11448 eVal == PDFWriter::Start || 11449 eVal == PDFWriter::End ) 11450 bInsert = true; 11451 break; 11452 case PDFWriter::WritingMode: 11453 if( eVal == PDFWriter::LrTb || 11454 eVal == PDFWriter::RlTb || 11455 eVal == PDFWriter::TbRl ) 11456 { 11457 bInsert = true; 11458 } 11459 break; 11460 case PDFWriter::TextAlign: 11461 if( eVal == PDFWriter::Start || 11462 eVal == PDFWriter::Center || 11463 eVal == PDFWriter::End || 11464 eVal == PDFWriter::Justify ) 11465 { 11466 if( eType == PDFWriter::Paragraph || 11467 eType == PDFWriter::Heading || 11468 eType == PDFWriter::H1 || 11469 eType == PDFWriter::H2 || 11470 eType == PDFWriter::H3 || 11471 eType == PDFWriter::H4 || 11472 eType == PDFWriter::H5 || 11473 eType == PDFWriter::H6 || 11474 eType == PDFWriter::List || 11475 eType == PDFWriter::ListItem || 11476 eType == PDFWriter::LILabel || 11477 eType == PDFWriter::LIBody || 11478 eType == PDFWriter::Table || 11479 eType == PDFWriter::TableRow || 11480 eType == PDFWriter::TableHeader || 11481 eType == PDFWriter::TableData ) 11482 { 11483 bInsert = true; 11484 } 11485 } 11486 break; 11487 case PDFWriter::Width: 11488 case PDFWriter::Height: 11489 if( eVal == PDFWriter::Auto ) 11490 { 11491 if( eType == PDFWriter::Figure || 11492 eType == PDFWriter::Formula || 11493 eType == PDFWriter::Form || 11494 eType == PDFWriter::Table || 11495 eType == PDFWriter::TableHeader || 11496 eType == PDFWriter::TableData ) 11497 { 11498 bInsert = true; 11499 } 11500 } 11501 break; 11502 case PDFWriter::BlockAlign: 11503 if( eVal == PDFWriter::Before || 11504 eVal == PDFWriter::Middle || 11505 eVal == PDFWriter::After || 11506 eVal == PDFWriter::Justify ) 11507 { 11508 if( eType == PDFWriter::TableHeader || 11509 eType == PDFWriter::TableData ) 11510 { 11511 bInsert = true; 11512 } 11513 } 11514 break; 11515 case PDFWriter::InlineAlign: 11516 if( eVal == PDFWriter::Start || 11517 eVal == PDFWriter::Center || 11518 eVal == PDFWriter::End ) 11519 { 11520 if( eType == PDFWriter::TableHeader || 11521 eType == PDFWriter::TableData ) 11522 { 11523 bInsert = true; 11524 } 11525 } 11526 break; 11527 case PDFWriter::LineHeight: 11528 if( eVal == PDFWriter::Normal || 11529 eVal == PDFWriter::Auto ) 11530 { 11531 // only for ILSE and BLSE 11532 if( eType == PDFWriter::Paragraph || 11533 eType == PDFWriter::Heading || 11534 eType == PDFWriter::H1 || 11535 eType == PDFWriter::H2 || 11536 eType == PDFWriter::H3 || 11537 eType == PDFWriter::H4 || 11538 eType == PDFWriter::H5 || 11539 eType == PDFWriter::H6 || 11540 eType == PDFWriter::List || 11541 eType == PDFWriter::ListItem || 11542 eType == PDFWriter::LILabel || 11543 eType == PDFWriter::LIBody || 11544 eType == PDFWriter::Table || 11545 eType == PDFWriter::TableRow || 11546 eType == PDFWriter::TableHeader || 11547 eType == PDFWriter::TableData || 11548 eType == PDFWriter::Span || 11549 eType == PDFWriter::Quote || 11550 eType == PDFWriter::Note || 11551 eType == PDFWriter::Reference || 11552 eType == PDFWriter::BibEntry || 11553 eType == PDFWriter::Code || 11554 eType == PDFWriter::Link ) 11555 { 11556 bInsert = true; 11557 } 11558 } 11559 break; 11560 case PDFWriter::TextDecorationType: 11561 if( eVal == PDFWriter::NONE || 11562 eVal == PDFWriter::Underline || 11563 eVal == PDFWriter::Overline || 11564 eVal == PDFWriter::LineThrough ) 11565 { 11566 // only for ILSE and BLSE 11567 if( eType == PDFWriter::Paragraph || 11568 eType == PDFWriter::Heading || 11569 eType == PDFWriter::H1 || 11570 eType == PDFWriter::H2 || 11571 eType == PDFWriter::H3 || 11572 eType == PDFWriter::H4 || 11573 eType == PDFWriter::H5 || 11574 eType == PDFWriter::H6 || 11575 eType == PDFWriter::List || 11576 eType == PDFWriter::ListItem || 11577 eType == PDFWriter::LILabel || 11578 eType == PDFWriter::LIBody || 11579 eType == PDFWriter::Table || 11580 eType == PDFWriter::TableRow || 11581 eType == PDFWriter::TableHeader || 11582 eType == PDFWriter::TableData || 11583 eType == PDFWriter::Span || 11584 eType == PDFWriter::Quote || 11585 eType == PDFWriter::Note || 11586 eType == PDFWriter::Reference || 11587 eType == PDFWriter::BibEntry || 11588 eType == PDFWriter::Code || 11589 eType == PDFWriter::Link ) 11590 { 11591 bInsert = true; 11592 } 11593 } 11594 break; 11595 case PDFWriter::ListNumbering: 11596 if( eVal == PDFWriter::NONE || 11597 eVal == PDFWriter::Disc || 11598 eVal == PDFWriter::Circle || 11599 eVal == PDFWriter::Square || 11600 eVal == PDFWriter::Decimal || 11601 eVal == PDFWriter::UpperRoman || 11602 eVal == PDFWriter::LowerRoman || 11603 eVal == PDFWriter::UpperAlpha || 11604 eVal == PDFWriter::LowerAlpha ) 11605 { 11606 if( eType == PDFWriter::List ) 11607 bInsert = true; 11608 } 11609 break; 11610 default: break; 11611 } 11612 } 11613 11614 if( bInsert ) 11615 m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( eVal ); 11616 #if OSL_DEBUG_LEVEL > 1 11617 else if( m_nCurrentStructElement > 0 && m_bEmitStructure ) 11618 fprintf( stderr, "rejecting setStructureAttribute( %s, %s ) on %s (%s) element\n", 11619 getAttributeTag( eAttr ), 11620 getAttributeValueTag( eVal ), 11621 getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ), 11622 m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr() 11623 ); 11624 #endif 11625 11626 return bInsert; 11627 } 11628 11629 bool PDFWriterImpl::setStructureAttributeNumerical( enum PDFWriter::StructAttribute eAttr, sal_Int32 nValue ) 11630 { 11631 if( ! m_aContext.Tagged ) 11632 return false; 11633 11634 bool bInsert = false; 11635 if( m_nCurrentStructElement > 0 && m_bEmitStructure ) 11636 { 11637 if( eAttr == PDFWriter::Language ) 11638 { 11639 m_aStructure[ m_nCurrentStructElement ].m_aLocale = MsLangId::convertLanguageToLocale( (LanguageType)nValue ); 11640 return true; 11641 } 11642 11643 PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType; 11644 switch( eAttr ) 11645 { 11646 case PDFWriter::SpaceBefore: 11647 case PDFWriter::SpaceAfter: 11648 case PDFWriter::StartIndent: 11649 case PDFWriter::EndIndent: 11650 // just for BLSE 11651 if( eType == PDFWriter::Paragraph || 11652 eType == PDFWriter::Heading || 11653 eType == PDFWriter::H1 || 11654 eType == PDFWriter::H2 || 11655 eType == PDFWriter::H3 || 11656 eType == PDFWriter::H4 || 11657 eType == PDFWriter::H5 || 11658 eType == PDFWriter::H6 || 11659 eType == PDFWriter::List || 11660 eType == PDFWriter::ListItem || 11661 eType == PDFWriter::LILabel || 11662 eType == PDFWriter::LIBody || 11663 eType == PDFWriter::Table || 11664 eType == PDFWriter::TableRow || 11665 eType == PDFWriter::TableHeader || 11666 eType == PDFWriter::TableData ) 11667 { 11668 bInsert = true; 11669 } 11670 break; 11671 case PDFWriter::TextIndent: 11672 // paragraph like BLSE and additional elements 11673 if( eType == PDFWriter::Paragraph || 11674 eType == PDFWriter::Heading || 11675 eType == PDFWriter::H1 || 11676 eType == PDFWriter::H2 || 11677 eType == PDFWriter::H3 || 11678 eType == PDFWriter::H4 || 11679 eType == PDFWriter::H5 || 11680 eType == PDFWriter::H6 || 11681 eType == PDFWriter::LILabel || 11682 eType == PDFWriter::LIBody || 11683 eType == PDFWriter::TableHeader || 11684 eType == PDFWriter::TableData ) 11685 { 11686 bInsert = true; 11687 } 11688 break; 11689 case PDFWriter::Width: 11690 case PDFWriter::Height: 11691 if( eType == PDFWriter::Figure || 11692 eType == PDFWriter::Formula || 11693 eType == PDFWriter::Form || 11694 eType == PDFWriter::Table || 11695 eType == PDFWriter::TableHeader || 11696 eType == PDFWriter::TableData ) 11697 { 11698 bInsert = true; 11699 } 11700 break; 11701 case PDFWriter::LineHeight: 11702 case PDFWriter::BaselineShift: 11703 // only for ILSE and BLSE 11704 if( eType == PDFWriter::Paragraph || 11705 eType == PDFWriter::Heading || 11706 eType == PDFWriter::H1 || 11707 eType == PDFWriter::H2 || 11708 eType == PDFWriter::H3 || 11709 eType == PDFWriter::H4 || 11710 eType == PDFWriter::H5 || 11711 eType == PDFWriter::H6 || 11712 eType == PDFWriter::List || 11713 eType == PDFWriter::ListItem || 11714 eType == PDFWriter::LILabel || 11715 eType == PDFWriter::LIBody || 11716 eType == PDFWriter::Table || 11717 eType == PDFWriter::TableRow || 11718 eType == PDFWriter::TableHeader || 11719 eType == PDFWriter::TableData || 11720 eType == PDFWriter::Span || 11721 eType == PDFWriter::Quote || 11722 eType == PDFWriter::Note || 11723 eType == PDFWriter::Reference || 11724 eType == PDFWriter::BibEntry || 11725 eType == PDFWriter::Code || 11726 eType == PDFWriter::Link ) 11727 { 11728 bInsert = true; 11729 } 11730 break; 11731 case PDFWriter::RowSpan: 11732 case PDFWriter::ColSpan: 11733 // only for table cells 11734 if( eType == PDFWriter::TableHeader || 11735 eType == PDFWriter::TableData ) 11736 { 11737 bInsert = true; 11738 } 11739 break; 11740 case PDFWriter::LinkAnnotation: 11741 if( eType == PDFWriter::Link ) 11742 bInsert = true; 11743 break; 11744 default: break; 11745 } 11746 } 11747 11748 if( bInsert ) 11749 m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( nValue ); 11750 #if OSL_DEBUG_LEVEL > 1 11751 else if( m_nCurrentStructElement > 0 && m_bEmitStructure ) 11752 fprintf( stderr, "rejecting setStructureAttributeNumerical( %s, %d ) on %s (%s) element\n", 11753 getAttributeTag( eAttr ), 11754 (int)nValue, 11755 getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ), 11756 m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr() ); 11757 #endif 11758 11759 return bInsert; 11760 } 11761 11762 void PDFWriterImpl::setStructureBoundingBox( const Rectangle& rRect ) 11763 { 11764 sal_Int32 nPageNr = m_nCurrentPage; 11765 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() || !m_aContext.Tagged ) 11766 return; 11767 11768 11769 if( m_nCurrentStructElement > 0 && m_bEmitStructure ) 11770 { 11771 PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType; 11772 if( eType == PDFWriter::Figure || 11773 eType == PDFWriter::Formula || 11774 eType == PDFWriter::Form || 11775 eType == PDFWriter::Table ) 11776 { 11777 m_aStructure[ m_nCurrentStructElement ].m_aBBox = rRect; 11778 // convert to default user space now, since the mapmode may change 11779 m_aPages[nPageNr].convertRect( m_aStructure[ m_nCurrentStructElement ].m_aBBox ); 11780 } 11781 } 11782 } 11783 11784 void PDFWriterImpl::setActualText( const String& rText ) 11785 { 11786 if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure ) 11787 { 11788 m_aStructure[ m_nCurrentStructElement ].m_aActualText = rText; 11789 } 11790 } 11791 11792 void PDFWriterImpl::setAlternateText( const String& rText ) 11793 { 11794 if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure ) 11795 { 11796 m_aStructure[ m_nCurrentStructElement ].m_aAltText = rText; 11797 } 11798 } 11799 11800 void PDFWriterImpl::setAutoAdvanceTime( sal_uInt32 nSeconds, sal_Int32 nPageNr ) 11801 { 11802 if( nPageNr < 0 ) 11803 nPageNr = m_nCurrentPage; 11804 11805 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 11806 return; 11807 11808 m_aPages[ nPageNr ].m_nDuration = nSeconds; 11809 } 11810 11811 void PDFWriterImpl::setPageTransition( PDFWriter::PageTransition eType, sal_uInt32 nMilliSec, sal_Int32 nPageNr ) 11812 { 11813 if( nPageNr < 0 ) 11814 nPageNr = m_nCurrentPage; 11815 11816 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 11817 return; 11818 11819 m_aPages[ nPageNr ].m_eTransition = eType; 11820 m_aPages[ nPageNr ].m_nTransTime = nMilliSec; 11821 } 11822 11823 void PDFWriterImpl::ensureUniqueRadioOnValues() 11824 { 11825 // loop over radio groups 11826 for( std::map<sal_Int32,sal_Int32>::const_iterator group = m_aRadioGroupWidgets.begin(); 11827 group != m_aRadioGroupWidgets.end(); ++group ) 11828 { 11829 PDFWidget& rGroupWidget = m_aWidgets[ group->second ]; 11830 // check whether all kids have a unique OnValue 11831 std::hash_map< OUString, sal_Int32, OUStringHash > aOnValues; 11832 int nChildren = rGroupWidget.m_aKidsIndex.size(); 11833 bool bIsUnique = true; 11834 for( int nKid = 0; nKid < nChildren && bIsUnique; nKid++ ) 11835 { 11836 int nKidIndex = rGroupWidget.m_aKidsIndex[nKid]; 11837 const OUString& rVal = m_aWidgets[nKidIndex].m_aOnValue; 11838 #if OSL_DEBUG_LEVEL > 1 11839 fprintf( stderr, "OnValue: %s\n", OUStringToOString( rVal, RTL_TEXTENCODING_UTF8 ).getStr() ); 11840 #endif 11841 if( aOnValues.find( rVal ) == aOnValues.end() ) 11842 { 11843 aOnValues[ rVal ] = 1; 11844 } 11845 else 11846 { 11847 bIsUnique = false; 11848 } 11849 } 11850 if( ! bIsUnique ) 11851 { 11852 #if OSL_DEBUG_LEVEL > 1 11853 fprintf( stderr, "enforcing unique OnValues\n" ); 11854 #endif 11855 // make unique by using ascending OnValues 11856 for( int nKid = 0; nKid < nChildren; nKid++ ) 11857 { 11858 int nKidIndex = rGroupWidget.m_aKidsIndex[nKid]; 11859 PDFWidget& rKid = m_aWidgets[nKidIndex]; 11860 rKid.m_aOnValue = OUString::valueOf( sal_Int32(nKid+1) ); 11861 if( ! rKid.m_aValue.equalsAscii( "Off" ) ) 11862 rKid.m_aValue = rKid.m_aOnValue; 11863 } 11864 } 11865 // finally move the "Yes" appearance to the OnValue appearance 11866 for( int nKid = 0; nKid < nChildren; nKid++ ) 11867 { 11868 int nKidIndex = rGroupWidget.m_aKidsIndex[nKid]; 11869 PDFWidget& rKid = m_aWidgets[nKidIndex]; 11870 PDFAppearanceMap::iterator app_it = rKid.m_aAppearances.find( "N" ); 11871 if( app_it != rKid.m_aAppearances.end() ) 11872 { 11873 PDFAppearanceStreams::iterator stream_it = app_it->second.find( "Yes" ); 11874 if( stream_it != app_it->second.end() ) 11875 { 11876 SvMemoryStream* pStream = stream_it->second; 11877 app_it->second.erase( stream_it ); 11878 OStringBuffer aBuf( rKid.m_aOnValue.getLength()*2 ); 11879 appendName( rKid.m_aOnValue, aBuf ); 11880 (app_it->second)[ aBuf.makeStringAndClear() ] = pStream; 11881 } 11882 #if OSL_DEBUG_LEVEL > 1 11883 else 11884 fprintf( stderr, "error: RadioButton without \"Yes\" stream\n" ); 11885 #endif 11886 } 11887 // update selected radio button 11888 if( ! rKid.m_aValue.equalsAscii( "Off" ) ) 11889 { 11890 rGroupWidget.m_aValue = rKid.m_aValue; 11891 } 11892 } 11893 } 11894 } 11895 11896 sal_Int32 PDFWriterImpl::findRadioGroupWidget( const PDFWriter::RadioButtonWidget& rBtn ) 11897 { 11898 sal_Int32 nRadioGroupWidget = -1; 11899 11900 std::map< sal_Int32, sal_Int32 >::const_iterator it = m_aRadioGroupWidgets.find( rBtn.RadioGroup ); 11901 11902 if( it == m_aRadioGroupWidgets.end() ) 11903 { 11904 m_aRadioGroupWidgets[ rBtn.RadioGroup ] = nRadioGroupWidget = 11905 sal_Int32(m_aWidgets.size()); 11906 11907 // new group, insert the radiobutton 11908 m_aWidgets.push_back( PDFWidget() ); 11909 m_aWidgets.back().m_nObject = createObject(); 11910 m_aWidgets.back().m_nPage = m_nCurrentPage; 11911 m_aWidgets.back().m_eType = PDFWriter::RadioButton; 11912 m_aWidgets.back().m_nRadioGroup = rBtn.RadioGroup; 11913 m_aWidgets.back().m_nFlags |= 0x0000C000; // NoToggleToOff and Radio bits 11914 11915 createWidgetFieldName( sal_Int32(m_aWidgets.size()-1), rBtn ); 11916 } 11917 else 11918 nRadioGroupWidget = it->second; 11919 11920 return nRadioGroupWidget; 11921 } 11922 11923 sal_Int32 PDFWriterImpl::createControl( const PDFWriter::AnyWidget& rControl, sal_Int32 nPageNr ) 11924 { 11925 if( nPageNr < 0 ) 11926 nPageNr = m_nCurrentPage; 11927 11928 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 11929 return -1; 11930 11931 sal_Int32 nNewWidget = m_aWidgets.size(); 11932 m_aWidgets.push_back( PDFWidget() ); 11933 11934 m_aWidgets.back().m_nObject = createObject(); 11935 m_aWidgets.back().m_aRect = rControl.Location; 11936 m_aWidgets.back().m_nPage = nPageNr; 11937 m_aWidgets.back().m_eType = rControl.getType(); 11938 11939 sal_Int32 nRadioGroupWidget = -1; 11940 // for unknown reasons the radio buttons of a radio group must not have a 11941 // field name, else the buttons are in fact check boxes - 11942 // that is multiple buttons of the radio group can be selected 11943 if( rControl.getType() == PDFWriter::RadioButton ) 11944 nRadioGroupWidget = findRadioGroupWidget( static_cast<const PDFWriter::RadioButtonWidget&>(rControl) ); 11945 else 11946 { 11947 createWidgetFieldName( nNewWidget, rControl ); 11948 } 11949 11950 // caution: m_aWidgets must not be changed after here or rNewWidget may be invalid 11951 PDFWidget& rNewWidget = m_aWidgets[nNewWidget]; 11952 rNewWidget.m_aDescription = rControl.Description; 11953 rNewWidget.m_aText = rControl.Text; 11954 rNewWidget.m_nTextStyle = rControl.TextStyle & 11955 ( TEXT_DRAW_LEFT | TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT | TEXT_DRAW_TOP | 11956 TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM | 11957 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK ); 11958 rNewWidget.m_nTabOrder = rControl.TabOrder; 11959 11960 // various properties are set via the flags (/Ff) property of the field dict 11961 if( rControl.ReadOnly ) 11962 rNewWidget.m_nFlags |= 1; 11963 if( rControl.getType() == PDFWriter::PushButton ) 11964 { 11965 const PDFWriter::PushButtonWidget& rBtn = static_cast<const PDFWriter::PushButtonWidget&>(rControl); 11966 if( rNewWidget.m_nTextStyle == 0 ) 11967 rNewWidget.m_nTextStyle = 11968 TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | 11969 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK; 11970 11971 rNewWidget.m_nFlags |= 0x00010000; 11972 if( rBtn.URL.getLength() ) 11973 rNewWidget.m_aListEntries.push_back( rBtn.URL ); 11974 rNewWidget.m_bSubmit = rBtn.Submit; 11975 rNewWidget.m_bSubmitGet = rBtn.SubmitGet; 11976 rNewWidget.m_nDest = rBtn.Dest; 11977 createDefaultPushButtonAppearance( rNewWidget, rBtn ); 11978 } 11979 else if( rControl.getType() == PDFWriter::RadioButton ) 11980 { 11981 const PDFWriter::RadioButtonWidget& rBtn = static_cast<const PDFWriter::RadioButtonWidget&>(rControl); 11982 if( rNewWidget.m_nTextStyle == 0 ) 11983 rNewWidget.m_nTextStyle = 11984 TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK; 11985 /* PDF sees a RadioButton group as one radio button with 11986 * children which are in turn check boxes 11987 * 11988 * so we need to create a radio button on demand for a new group 11989 * and insert a checkbox for each RadioButtonWidget as its child 11990 */ 11991 rNewWidget.m_eType = PDFWriter::CheckBox; 11992 rNewWidget.m_nRadioGroup = rBtn.RadioGroup; 11993 11994 DBG_ASSERT( nRadioGroupWidget >= 0 && nRadioGroupWidget < (sal_Int32)m_aWidgets.size(), "no radio group parent" ); 11995 11996 PDFWidget& rRadioButton = m_aWidgets[nRadioGroupWidget]; 11997 rRadioButton.m_aKids.push_back( rNewWidget.m_nObject ); 11998 rRadioButton.m_aKidsIndex.push_back( nNewWidget ); 11999 rNewWidget.m_nParent = rRadioButton.m_nObject; 12000 12001 rNewWidget.m_aValue = OUString( RTL_CONSTASCII_USTRINGPARAM( "Off" ) ); 12002 rNewWidget.m_aOnValue = rBtn.OnValue; 12003 if( ! rRadioButton.m_aValue.getLength() && rBtn.Selected ) 12004 { 12005 rNewWidget.m_aValue = rNewWidget.m_aOnValue; 12006 rRadioButton.m_aValue = rNewWidget.m_aOnValue; 12007 } 12008 createDefaultRadioButtonAppearance( rNewWidget, rBtn ); 12009 12010 // union rect of radio group 12011 Rectangle aRect = rNewWidget.m_aRect; 12012 m_aPages[ nPageNr ].convertRect( aRect ); 12013 rRadioButton.m_aRect.Union( aRect ); 12014 } 12015 else if( rControl.getType() == PDFWriter::CheckBox ) 12016 { 12017 const PDFWriter::CheckBoxWidget& rBox = static_cast<const PDFWriter::CheckBoxWidget&>(rControl); 12018 if( rNewWidget.m_nTextStyle == 0 ) 12019 rNewWidget.m_nTextStyle = 12020 TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK; 12021 12022 rNewWidget.m_aValue = OUString::createFromAscii( rBox.Checked ? "Yes" : "Off" ); 12023 // create default appearance before m_aRect gets transformed 12024 createDefaultCheckBoxAppearance( rNewWidget, rBox ); 12025 } 12026 else if( rControl.getType() == PDFWriter::ListBox ) 12027 { 12028 if( rNewWidget.m_nTextStyle == 0 ) 12029 rNewWidget.m_nTextStyle = TEXT_DRAW_VCENTER; 12030 12031 const PDFWriter::ListBoxWidget& rLstBox = static_cast<const PDFWriter::ListBoxWidget&>(rControl); 12032 rNewWidget.m_aListEntries = rLstBox.Entries; 12033 rNewWidget.m_aSelectedEntries = rLstBox.SelectedEntries; 12034 rNewWidget.m_aValue = rLstBox.Text; 12035 if( rLstBox.DropDown ) 12036 rNewWidget.m_nFlags |= 0x00020000; 12037 if( rLstBox.Sort ) 12038 rNewWidget.m_nFlags |= 0x00080000; 12039 if( rLstBox.MultiSelect && !rLstBox.DropDown && (int)m_aContext.Version > (int)PDFWriter::PDF_1_3 ) 12040 rNewWidget.m_nFlags |= 0x00200000; 12041 12042 createDefaultListBoxAppearance( rNewWidget, rLstBox ); 12043 } 12044 else if( rControl.getType() == PDFWriter::ComboBox ) 12045 { 12046 if( rNewWidget.m_nTextStyle == 0 ) 12047 rNewWidget.m_nTextStyle = TEXT_DRAW_VCENTER; 12048 12049 const PDFWriter::ComboBoxWidget& rBox = static_cast<const PDFWriter::ComboBoxWidget&>(rControl); 12050 rNewWidget.m_aValue = rBox.Text; 12051 rNewWidget.m_aListEntries = rBox.Entries; 12052 rNewWidget.m_nFlags |= 0x00060000; // combo and edit flag 12053 if( rBox.Sort ) 12054 rNewWidget.m_nFlags |= 0x00080000; 12055 12056 PDFWriter::ListBoxWidget aLBox; 12057 aLBox.Name = rBox.Name; 12058 aLBox.Description = rBox.Description; 12059 aLBox.Text = rBox.Text; 12060 aLBox.TextStyle = rBox.TextStyle; 12061 aLBox.ReadOnly = rBox.ReadOnly; 12062 aLBox.Border = rBox.Border; 12063 aLBox.BorderColor = rBox.BorderColor; 12064 aLBox.Background = rBox.Background; 12065 aLBox.BackgroundColor = rBox.BackgroundColor; 12066 aLBox.TextFont = rBox.TextFont; 12067 aLBox.TextColor = rBox.TextColor; 12068 aLBox.DropDown = true; 12069 aLBox.Sort = rBox.Sort; 12070 aLBox.MultiSelect = false; 12071 aLBox.Entries = rBox.Entries; 12072 12073 createDefaultListBoxAppearance( rNewWidget, aLBox ); 12074 } 12075 else if( rControl.getType() == PDFWriter::Edit ) 12076 { 12077 if( rNewWidget.m_nTextStyle == 0 ) 12078 rNewWidget.m_nTextStyle = TEXT_DRAW_LEFT | TEXT_DRAW_VCENTER; 12079 12080 const PDFWriter::EditWidget& rEdit = static_cast<const PDFWriter::EditWidget&>(rControl); 12081 if( rEdit.MultiLine ) 12082 { 12083 rNewWidget.m_nFlags |= 0x00001000; 12084 rNewWidget.m_nTextStyle |= TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK; 12085 } 12086 if( rEdit.Password ) 12087 rNewWidget.m_nFlags |= 0x00002000; 12088 if( rEdit.FileSelect && m_aContext.Version > PDFWriter::PDF_1_3 ) 12089 rNewWidget.m_nFlags |= 0x00100000; 12090 rNewWidget.m_nMaxLen = rEdit.MaxLen; 12091 rNewWidget.m_aValue = rEdit.Text; 12092 12093 createDefaultEditAppearance( rNewWidget, rEdit ); 12094 } 12095 12096 // convert to default user space now, since the mapmode may change 12097 // note: create default appearances before m_aRect gets transformed 12098 m_aPages[ nPageNr ].convertRect( rNewWidget.m_aRect ); 12099 12100 // insert widget to page's annotation list 12101 m_aPages[ nPageNr ].m_aAnnotations.push_back( rNewWidget.m_nObject ); 12102 12103 // mark page as having widgets 12104 m_aPages[ nPageNr ].m_bHasWidgets = true; 12105 12106 return nNewWidget; 12107 } 12108 12109 void PDFWriterImpl::beginControlAppearance( sal_Int32 nControl ) 12110 { 12111 if( nControl < 0 || nControl >= (sal_Int32)m_aWidgets.size() ) 12112 return; 12113 12114 PDFWidget& rWidget = m_aWidgets[ nControl ]; 12115 m_nCurrentControl = nControl; 12116 12117 SvMemoryStream* pControlStream = new SvMemoryStream( 1024, 1024 ); 12118 // back conversion of control rect to current MapMode; necessary because 12119 // MapMode between createControl and beginControlAppearance 12120 // could have changed; therefore the widget rectangle is 12121 // already converted 12122 Rectangle aBack( Point( rWidget.m_aRect.Left(), pointToPixel(m_aPages[m_nCurrentPage].getHeight()) - rWidget.m_aRect.Top() - rWidget.m_aRect.GetHeight() ), 12123 rWidget.m_aRect.GetSize() ); 12124 aBack = lcl_convert( m_aMapMode, 12125 m_aGraphicsStack.front().m_aMapMode, 12126 getReferenceDevice(), 12127 aBack ); 12128 beginRedirect( pControlStream, aBack ); 12129 writeBuffer( "/Tx BMC\n", 8 ); 12130 } 12131 12132 bool PDFWriterImpl::endControlAppearance( PDFWriter::WidgetState eState ) 12133 { 12134 bool bRet = false; 12135 if( ! m_aOutputStreams.empty() ) 12136 writeBuffer( "\nEMC\n", 5 ); 12137 SvMemoryStream* pAppearance = static_cast<SvMemoryStream*>(endRedirect()); 12138 if( pAppearance && m_nCurrentControl >= 0 && m_nCurrentControl < (sal_Int32)m_aWidgets.size() ) 12139 { 12140 PDFWidget& rWidget = m_aWidgets[ m_nCurrentControl ]; 12141 OString aState, aStyle; 12142 switch( rWidget.m_eType ) 12143 { 12144 case PDFWriter::PushButton: 12145 if( eState == PDFWriter::Up || eState == PDFWriter::Down ) 12146 { 12147 aState = (eState == PDFWriter::Up) ? "N" : "D"; 12148 aStyle = "Standard"; 12149 } 12150 break; 12151 case PDFWriter::CheckBox: 12152 if( eState == PDFWriter::Up || eState == PDFWriter::Down ) 12153 { 12154 aState = "N"; 12155 aStyle = (eState == PDFWriter::Up) ? "Off" : "Yes"; 12156 /* cf PDFReference 3rd ed. V1.4 p539: 12157 recommended name for on state is "Yes", 12158 recommended name for off state is "Off" 12159 */ 12160 } 12161 break; 12162 case PDFWriter::RadioButton: 12163 if( eState == PDFWriter::Up || eState == PDFWriter::Down ) 12164 { 12165 aState = "N"; 12166 if( eState == PDFWriter::Up ) 12167 aStyle = "Off"; 12168 else 12169 { 12170 OStringBuffer aBuf( rWidget.m_aOnValue.getLength()*2 ); 12171 appendName( rWidget.m_aOnValue, aBuf ); 12172 aStyle = aBuf.makeStringAndClear(); 12173 } 12174 } 12175 break; 12176 case PDFWriter::Edit: 12177 aState = "N"; 12178 aStyle = "Standard"; 12179 break; 12180 case PDFWriter::ListBox: 12181 case PDFWriter::ComboBox: 12182 case PDFWriter::Hierarchy: 12183 break; 12184 } 12185 if( aState.getLength() && aStyle.getLength() ) 12186 { 12187 // delete eventual existing stream 12188 PDFAppearanceStreams::iterator it = 12189 rWidget.m_aAppearances[ aState ].find( aStyle ); 12190 if( it != rWidget.m_aAppearances[ aState ].end() ) 12191 delete it->second; 12192 rWidget.m_aAppearances[ aState ][ aStyle ] = pAppearance; 12193 bRet = true; 12194 } 12195 } 12196 12197 if( ! bRet ) 12198 delete pAppearance; 12199 12200 m_nCurrentControl = -1; 12201 12202 return bRet; 12203 } 12204 12205 void PDFWriterImpl::addStream( const String& rMimeType, PDFOutputStream* pStream, bool bCompress ) 12206 { 12207 if( pStream ) 12208 { 12209 m_aAdditionalStreams.push_back( PDFAddStream() ); 12210 PDFAddStream& rStream = m_aAdditionalStreams.back(); 12211 rStream.m_aMimeType = rMimeType.Len() 12212 ? OUString( rMimeType ) 12213 : OUString( RTL_CONSTASCII_USTRINGPARAM( "application/octet-stream" ) ); 12214 rStream.m_pStream = pStream; 12215 rStream.m_bCompress = bCompress; 12216 } 12217 } 12218 12219 12220 12221