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