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