LibreOffice Module vcl (master)  1
pdfwriter_impl.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  * Licensed to the Apache Software Foundation (ASF) under one or more
12  * contributor license agreements. See the NOTICE file distributed
13  * with this work for additional information regarding copyright
14  * ownership. The ASF licenses this file to you under the Apache
15  * License, Version 2.0 (the "License"); you may not use this file
16  * except in compliance with the License. You may obtain a copy of
17  * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <config_features.h>
21 
22 #include <sal/types.h>
23 
24 #include <math.h>
25 #include <algorithm>
26 
27 #include <lcms2.h>
28 
35 #include <memory>
36 #include <com/sun/star/io/XOutputStream.hpp>
37 #include <com/sun/star/util/URL.hpp>
38 #include <com/sun/star/util/URLTransformer.hpp>
40 #include <comphelper/string.hxx>
41 #include <cppuhelper/implbase.hxx>
43 #include <o3tl/numeric.hxx>
44 #include <officecfg/Office/Common.hxx>
45 #include <osl/file.hxx>
46 #include <osl/thread.h>
47 #include <rtl/digest.h>
48 #include <rtl/ustrbuf.hxx>
49 #include <sal/log.hxx>
50 #include <svl/urihelper.hxx>
51 #include <tools/fract.hxx>
52 #include <tools/helpers.hxx>
53 #include <tools/stream.hxx>
54 #include <tools/urlobj.hxx>
55 #include <tools/zcodec.hxx>
56 #include <svl/cryptosign.hxx>
57 #include <vcl/bitmapex.hxx>
58 #include <vcl/bitmapaccess.hxx>
59 #include <vcl/canvastools.hxx>
60 #include <vcl/cvtgrf.hxx>
61 #include <vcl/fontcharmap.hxx>
62 #include <vcl/lineinfo.hxx>
63 #include <vcl/metric.hxx>
64 #include <vcl/settings.hxx>
65 #include <strhelper.hxx>
66 #include <vcl/svapp.hxx>
67 #include <vcl/virdev.hxx>
69 #include <comphelper/hash.hxx>
70 
71 #include <fontsubset.hxx>
72 #include <PhysicalFontFace.hxx>
73 #include <salgdi.hxx>
74 #include <textlayout.hxx>
75 #include <textlineinfo.hxx>
76 #include <bitmapwriteaccess.hxx>
77 #include <impglyphitem.hxx>
78 #include <pdf/XmpMetadata.hxx>
79 #include <pdf/objectcopier.hxx>
80 
81 #include "pdfwriter_impl.hxx"
82 
83 #ifdef _WIN32
84 // WinCrypt headers for PDF signing
85 // Note: this uses Windows 7 APIs and requires the relevant data types
86 #include <prewin.h>
87 #include <wincrypt.h>
88 #include <postwin.h>
89 #endif
90 
91 #include <config_eot.h>
92 
93 #if ENABLE_EOT
94 #include <libeot/libeot.h>
95 #endif
96 
97 using namespace::com::sun::star;
98 
99 static bool g_bDebugDisableCompression = getenv("VCL_DEBUG_DISABLE_PDFCOMPRESSION");
100 
101 #if HAVE_FEATURE_NSS
102 // Is this length truly the maximum possible, or just a number that
103 // seemed large enough when the author tested this (with some type of
104 // certificates)? I suspect the latter.
105 
106 // Used to be 0x4000 = 16384, but a sample signed PDF (produced by
107 // some other software) provided by the customer has a signature
108 // content that is 30000 bytes. The SampleSignedPDFDocument.pdf from
109 // Adobe has one that is 21942 bytes. So let's be careful. Pity this
110 // can't be dynamic, at least not without restructuring the code. Also
111 // note that the checks in the code for this being too small
112 // apparently are broken, if this overflows you end up with an invalid
113 // PDF. Need to fix that.
114 
115 #define MAX_SIGNATURE_CONTENT_LENGTH 50000
116 #endif
117 
118 static const sal_Int32 nLog10Divisor = 3;
119 static const double fDivisor = 1000.0;
120 
121 static double pixelToPoint( double px ) { return px/fDivisor; }
122 static sal_Int32 pointToPixel( double pt ) { return sal_Int32(pt*fDivisor); }
123 
124 static void appendHex( sal_Int8 nInt, OStringBuffer& rBuffer )
125 {
126  static const char pHexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7',
127  '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
128  rBuffer.append( pHexDigits[ (nInt >> 4) & 15 ] );
129  rBuffer.append( pHexDigits[ nInt & 15 ] );
130 }
131 
132 static void appendName( const OUString& rStr, OStringBuffer& rBuffer )
133 {
134 // FIXME i59651 add a check for max length of 127 chars? Per PDF spec 1.4, appendix C.1
135 // I guess than when reading the #xx sequence it will count for a single character.
136  OString aStr( OUStringToOString( rStr, RTL_TEXTENCODING_UTF8 ) );
137  int nLen = aStr.getLength();
138  for( int i = 0; i < nLen; i++ )
139  {
140  /* #i16920# PDF recommendation: output UTF8, any byte
141  * outside the interval [33(=ASCII'!');126(=ASCII'~')]
142  * should be escaped hexadecimal
143  * for the sake of ghostscript which also reads PDF
144  * but has a narrower acceptance rate we only pass
145  * alphanumerics and '-' literally.
146  */
147  if( (aStr[i] >= 'A' && aStr[i] <= 'Z' ) ||
148  (aStr[i] >= 'a' && aStr[i] <= 'z' ) ||
149  (aStr[i] >= '0' && aStr[i] <= '9' ) ||
150  aStr[i] == '-' )
151  {
152  rBuffer.append( aStr[i] );
153  }
154  else
155  {
156  rBuffer.append( '#' );
157  appendHex( static_cast<sal_Int8>(aStr[i]), rBuffer );
158  }
159  }
160 }
161 
162 static void appendName( const char* pStr, OStringBuffer& rBuffer )
163 {
164  // FIXME i59651 see above
165  while( pStr && *pStr )
166  {
167  if( (*pStr >= 'A' && *pStr <= 'Z' ) ||
168  (*pStr >= 'a' && *pStr <= 'z' ) ||
169  (*pStr >= '0' && *pStr <= '9' ) ||
170  *pStr == '-' )
171  {
172  rBuffer.append( *pStr );
173  }
174  else
175  {
176  rBuffer.append( '#' );
177  appendHex( static_cast<sal_Int8>(*pStr), rBuffer );
178  }
179  pStr++;
180  }
181 }
182 
183 //used only to emit encoded passwords
184 static void appendLiteralString( const char* pStr, sal_Int32 nLength, OStringBuffer& rBuffer )
185 {
186  while( nLength )
187  {
188  switch( *pStr )
189  {
190  case '\n' :
191  rBuffer.append( "\\n" );
192  break;
193  case '\r' :
194  rBuffer.append( "\\r" );
195  break;
196  case '\t' :
197  rBuffer.append( "\\t" );
198  break;
199  case '\b' :
200  rBuffer.append( "\\b" );
201  break;
202  case '\f' :
203  rBuffer.append( "\\f" );
204  break;
205  case '(' :
206  case ')' :
207  case '\\' :
208  rBuffer.append( "\\" );
209  rBuffer.append( static_cast<char>(*pStr) );
210  break;
211  default:
212  rBuffer.append( static_cast<char>(*pStr) );
213  break;
214  }
215  pStr++;
216  nLength--;
217  }
218 }
219 
220 /*
221  * Convert a string before using it.
222  *
223  * This string conversion function is needed because the destination name
224  * in a PDF file seen through an Internet browser should be
225  * specially crafted, in order to be used directly by the browser.
226  * In this way the fragment part of a hyperlink to a PDF file (e.g. something
227  * as 'test1/test2/a-file.pdf\#thefragment) will be (hopefully) interpreted by the
228  * PDF reader (currently only Adobe Reader plug-in seems to be working that way) called
229  * from inside the Internet browser as: 'open the file test1/test2/a-file.pdf
230  * and go to named destination thefragment using default zoom'.
231  * The conversion is needed because in case of a fragment in the form: Slide%201
232  * (meaning Slide 1) as it is converted obeying the Inet rules, it will become Slide25201
233  * using this conversion, in both the generated named destinations, fragment and GoToR
234  * destination.
235  *
236  * The names for destinations are name objects and so they don't need to be encrypted
237  * even though they expose the content of PDF file (e.g. guessing the PDF content from the
238  * destination name).
239  *
240  * Further limitation: it is advisable to use standard ASCII characters for
241  * OOo bookmarks.
242 */
243 static void appendDestinationName( const OUString& rString, OStringBuffer& rBuffer )
244 {
245  const sal_Unicode* pStr = rString.getStr();
246  sal_Int32 nLen = rString.getLength();
247  for( int i = 0; i < nLen; i++ )
248  {
249  sal_Unicode aChar = pStr[i];
250  if( (aChar >= '0' && aChar <= '9' ) ||
251  (aChar >= 'a' && aChar <= 'z' ) ||
252  (aChar >= 'A' && aChar <= 'Z' ) ||
253  aChar == '-' )
254  {
255  rBuffer.append(static_cast<char>(aChar));
256  }
257  else
258  {
259  sal_Int8 aValueHigh = sal_Int8(aChar >> 8);
260  if(aValueHigh > 0)
261  appendHex( aValueHigh, rBuffer );
262  appendHex( static_cast<sal_Int8>(aChar & 255 ), rBuffer );
263  }
264  }
265 }
266 
267 namespace vcl
268 {
270 {
271  0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
272  0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A
273 };
274 
275 template < class GEOMETRY >
276 static GEOMETRY lcl_convert( const MapMode& _rSource, const MapMode& _rDest, OutputDevice* _pPixelConversion, const GEOMETRY& _rObject )
277 {
278  GEOMETRY aPoint;
279  if ( MapUnit::MapPixel == _rSource.GetMapUnit() )
280  {
281  aPoint = _pPixelConversion->PixelToLogic( _rObject, _rDest );
282  }
283  else
284  {
285  aPoint = OutputDevice::LogicToLogic( _rObject, _rSource, _rDest );
286  }
287  return aPoint;
288 }
289 
290 void PDFWriter::AppendUnicodeTextString(const OUString& rString, OStringBuffer& rBuffer)
291 {
292  rBuffer.append( "FEFF" );
293  const sal_Unicode* pStr = rString.getStr();
294  sal_Int32 nLen = rString.getLength();
295  for( int i = 0; i < nLen; i++ )
296  {
297  sal_Unicode aChar = pStr[i];
298  appendHex( static_cast<sal_Int8>(aChar >> 8), rBuffer );
299  appendHex( static_cast<sal_Int8>(aChar & 255 ), rBuffer );
300  }
301 }
302 
303 void PDFWriterImpl::createWidgetFieldName( sal_Int32 i_nWidgetIndex, const PDFWriter::AnyWidget& i_rControl )
304 {
305  /* #i80258# previously we use appendName here
306  however we need a slightly different coding scheme than the normal
307  name encoding for field names
308  */
309  const OUString& rName = (m_aContext.Version > PDFWriter::PDFVersion::PDF_1_2) ? i_rControl.Name : i_rControl.Text;
310  OString aStr( OUStringToOString( rName, RTL_TEXTENCODING_UTF8 ) );
311  int nLen = aStr.getLength();
312 
313  OStringBuffer aBuffer( rName.getLength()+64 );
314  for( int i = 0; i < nLen; i++ )
315  {
316  /* #i16920# PDF recommendation: output UTF8, any byte
317  * outside the interval [32(=ASCII' ');126(=ASCII'~')]
318  * should be escaped hexadecimal
319  */
320  if( aStr[i] >= 32 && aStr[i] <= 126 )
321  aBuffer.append( aStr[i] );
322  else
323  {
324  aBuffer.append( '#' );
325  appendHex( static_cast<sal_Int8>(aStr[i]), aBuffer );
326  }
327  }
328 
329  OString aFullName( aBuffer.makeStringAndClear() );
330 
331  /* #i82785# create hierarchical fields down to the for each dot in i_rName */
332  sal_Int32 nTokenIndex = 0, nLastTokenIndex = 0;
333  OString aPartialName;
334  OString aDomain;
335  do
336  {
337  nLastTokenIndex = nTokenIndex;
338  aPartialName = aFullName.getToken( 0, '.', nTokenIndex );
339  if( nTokenIndex != -1 )
340  {
341  // find or create a hierarchical field
342  // first find the fully qualified name up to this field
343  aDomain = aFullName.copy( 0, nTokenIndex-1 );
344  std::unordered_map< OString, sal_Int32 >::const_iterator it = m_aFieldNameMap.find( aDomain );
345  if( it == m_aFieldNameMap.end() )
346  {
347  // create new hierarchy field
348  sal_Int32 nNewWidget = m_aWidgets.size();
349  m_aWidgets.emplace_back( );
350  m_aWidgets[nNewWidget].m_nObject = createObject();
351  m_aWidgets[nNewWidget].m_eType = PDFWriter::Hierarchy;
352  m_aWidgets[nNewWidget].m_aName = aPartialName;
353  m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[nNewWidget].m_nObject;
354  m_aFieldNameMap[aDomain] = nNewWidget;
355  m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[nNewWidget].m_nObject;
356  if( nLastTokenIndex > 0 )
357  {
358  // this field is not a root field and
359  // needs to be inserted to its parent
360  OString aParentDomain( aDomain.copy( 0, nLastTokenIndex-1 ) );
361  it = m_aFieldNameMap.find( aParentDomain );
362  OSL_ENSURE( it != m_aFieldNameMap.end(), "field name not found" );
363  if( it != m_aFieldNameMap.end() )
364  {
365  OSL_ENSURE( it->second < sal_Int32(m_aWidgets.size()), "invalid field number entry" );
366  if( it->second < sal_Int32(m_aWidgets.size()) )
367  {
368  PDFWidget& rParentField( m_aWidgets[it->second] );
369  rParentField.m_aKids.push_back( m_aWidgets[nNewWidget].m_nObject );
370  rParentField.m_aKidsIndex.push_back( nNewWidget );
371  m_aWidgets[nNewWidget].m_nParent = rParentField.m_nObject;
372  }
373  }
374  }
375  }
376  else if( m_aWidgets[it->second].m_eType != PDFWriter::Hierarchy )
377  {
378  // this is invalid, someone tries to have a terminal field as parent
379  // example: a button with the name foo.bar exists and
380  // another button is named foo.bar.no
381  // workaround: put the second terminal field as much up in the hierarchy as
382  // necessary to have a non-terminal field as parent (or none at all)
383  // since it->second already is terminal, we just need to use its parent
384  aDomain.clear();
385  aPartialName = aFullName.copy( aFullName.lastIndexOf( '.' )+1 );
386  if( nLastTokenIndex > 0 )
387  {
388  aDomain = aFullName.copy( 0, nLastTokenIndex-1 );
389  aFullName = aDomain + "." + aPartialName;
390  }
391  else
392  aFullName = aPartialName;
393  break;
394  }
395  }
396  } while( nTokenIndex != -1 );
397 
398  // insert widget into its hierarchy field
399  if( !aDomain.isEmpty() )
400  {
401  std::unordered_map< OString, sal_Int32 >::const_iterator it = m_aFieldNameMap.find( aDomain );
402  if( it != m_aFieldNameMap.end() )
403  {
404  OSL_ENSURE( it->second >= 0 && it->second < sal_Int32( m_aWidgets.size() ), "invalid field index" );
405  if( it->second >= 0 && it->second < sal_Int32(m_aWidgets.size()) )
406  {
407  m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[it->second].m_nObject;
408  m_aWidgets[it->second].m_aKids.push_back( m_aWidgets[i_nWidgetIndex].m_nObject);
409  m_aWidgets[it->second].m_aKidsIndex.push_back( i_nWidgetIndex );
410  }
411  }
412  }
413 
414  if( aPartialName.isEmpty() )
415  {
416  // how funny, an empty field name
417  if( i_rControl.getType() == PDFWriter::RadioButton )
418  {
419  aPartialName = "RadioGroup" +
420  OString::number( static_cast<const PDFWriter::RadioButtonWidget&>(i_rControl).RadioGroup );
421  }
422  else
423  aPartialName = OString( "Widget" );
424  }
425 
427  {
428  std::unordered_map<OString, sal_Int32>::iterator it = m_aFieldNameMap.find( aFullName );
429 
430  if( it != m_aFieldNameMap.end() ) // not unique
431  {
432  std::unordered_map< OString, sal_Int32 >::const_iterator check_it;
433  OString aTry;
434  sal_Int32 nTry = 2;
435  do
436  {
437  OStringBuffer aUnique( aFullName.getLength() + 16 );
438  aUnique.append( aFullName );
439  aUnique.append( '_' );
440  aUnique.append( nTry++ );
441  aTry = aUnique.makeStringAndClear();
442  check_it = m_aFieldNameMap.find( aTry );
443  } while( check_it != m_aFieldNameMap.end() );
444  aFullName = aTry;
445  m_aFieldNameMap[ aFullName ] = i_nWidgetIndex;
446  aPartialName = aFullName.copy( aFullName.lastIndexOf( '.' )+1 );
447  }
448  else
449  m_aFieldNameMap[ aFullName ] = i_nWidgetIndex;
450  }
451 
452  // finally
453  m_aWidgets[i_nWidgetIndex].m_aName = aPartialName;
454 }
455 
456 static void appendFixedInt( sal_Int32 nValue, OStringBuffer& rBuffer )
457 {
458  if( nValue < 0 )
459  {
460  rBuffer.append( '-' );
461  nValue = -nValue;
462  }
463  sal_Int32 nFactor = 1, nDiv = nLog10Divisor;
464  while( nDiv-- )
465  nFactor *= 10;
466 
467  sal_Int32 nInt = nValue / nFactor;
468  rBuffer.append( nInt );
469  if (nFactor > 1 && nValue % nFactor)
470  {
471  rBuffer.append( '.' );
472  do
473  {
474  nFactor /= 10;
475  rBuffer.append((nValue / nFactor) % 10);
476  }
477  while (nFactor > 1 && nValue % nFactor); // omit trailing zeros
478  }
479 }
480 
481 // appends a double. PDF does not accept exponential format, only fixed point
482 static void appendDouble( double fValue, OStringBuffer& rBuffer, sal_Int32 nPrecision = 5 )
483 {
484  bool bNeg = false;
485  if( fValue < 0.0 )
486  {
487  bNeg = true;
488  fValue=-fValue;
489  }
490 
491  sal_Int64 nInt = static_cast<sal_Int64>(fValue);
492  fValue -= static_cast<double>(nInt);
493  // optimizing hardware may lead to a value of 1.0 after the subtraction
494  if( rtl::math::approxEqual(fValue, 1.0) || log10( 1.0-fValue ) <= -nPrecision )
495  {
496  nInt++;
497  fValue = 0.0;
498  }
499  sal_Int64 nFrac = 0;
500  if( fValue )
501  {
502  fValue *= pow( 10.0, static_cast<double>(nPrecision) );
503  nFrac = static_cast<sal_Int64>(fValue);
504  }
505  if( bNeg && ( nInt || nFrac ) )
506  rBuffer.append( '-' );
507  rBuffer.append( nInt );
508  if( nFrac )
509  {
510  int i;
511  rBuffer.append( '.' );
512  sal_Int64 nBound = static_cast<sal_Int64>(pow( 10.0, nPrecision - 1.0 )+0.5);
513  for ( i = 0; ( i < nPrecision ) && nFrac; i++ )
514  {
515  sal_Int64 nNumb = nFrac / nBound;
516  nFrac -= nNumb * nBound;
517  rBuffer.append( nNumb );
518  nBound /= 10;
519  }
520  }
521 }
522 
523 static void appendColor( const Color& rColor, OStringBuffer& rBuffer, bool bConvertToGrey )
524 {
525 
526  if( rColor != COL_TRANSPARENT )
527  {
528  if( bConvertToGrey )
529  {
530  sal_uInt8 cByte = rColor.GetLuminance();
531  appendDouble( static_cast<double>(cByte) / 255.0, rBuffer );
532  }
533  else
534  {
535  appendDouble( static_cast<double>(rColor.GetRed()) / 255.0, rBuffer );
536  rBuffer.append( ' ' );
537  appendDouble( static_cast<double>(rColor.GetGreen()) / 255.0, rBuffer );
538  rBuffer.append( ' ' );
539  appendDouble( static_cast<double>(rColor.GetBlue()) / 255.0, rBuffer );
540  }
541  }
542 }
543 
544 void PDFWriterImpl::appendStrokingColor( const Color& rColor, OStringBuffer& rBuffer )
545 {
546  if( rColor != COL_TRANSPARENT )
547  {
549  appendColor( rColor, rBuffer, bGrey );
550  rBuffer.append( bGrey ? " G" : " RG" );
551  }
552 }
553 
554 void PDFWriterImpl::appendNonStrokingColor( const Color& rColor, OStringBuffer& rBuffer )
555 {
556  if( rColor != COL_TRANSPARENT )
557  {
559  appendColor( rColor, rBuffer, bGrey );
560  rBuffer.append( bGrey ? " g" : " rg" );
561  }
562 }
563 
564 namespace
565 {
566 
567 void appendPdfTimeDate(OStringBuffer & rBuffer,
568  sal_Int16 year, sal_uInt16 month, sal_uInt16 day, sal_uInt16 hours, sal_uInt16 minutes, sal_uInt16 seconds, sal_uInt32 tzDelta)
569 {
570  rBuffer.append("D:");
571  rBuffer.append(char('0' + ((year / 1000) % 10)));
572  rBuffer.append(char('0' + ((year / 100) % 10)));
573  rBuffer.append(char('0' + ((year / 10) % 10)));
574  rBuffer.append(char('0' + (year % 10)));
575  rBuffer.append(char('0' + ((month / 10) % 10)));
576  rBuffer.append(char('0' + (month % 10)));
577  rBuffer.append(char('0' + ((day / 10) % 10)));
578  rBuffer.append(char('0' + (day % 10)));
579  rBuffer.append(char('0' + ((hours / 10) % 10)));
580  rBuffer.append(char('0' + (hours % 10)));
581  rBuffer.append(char('0' + ((minutes / 10) % 10)));
582  rBuffer.append(char('0' + (minutes % 10)));
583  rBuffer.append(char('0' + ((seconds / 10) % 10)));
584  rBuffer.append(char('0' + (seconds % 10)));
585 
586  if (tzDelta == 0)
587  {
588  rBuffer.append("Z");
589  }
590  else
591  {
592  if (tzDelta > 0 )
593  rBuffer.append("+");
594  else
595  rBuffer.append("-");
596 
597  rBuffer.append(char('0' + ((tzDelta / 36000) % 10)));
598  rBuffer.append(char('0' + ((tzDelta / 3600) % 10)));
599  rBuffer.append("'");
600  rBuffer.append(char('0' + ((tzDelta / 600) % 6)));
601  rBuffer.append(char('0' + ((tzDelta / 60) % 10)));
602  }
603 }
604 
605 } // end namespace
606 
607 PDFPage::PDFPage( PDFWriterImpl* pWriter, double nPageWidth, double nPageHeight, PDFWriter::Orientation eOrientation )
608  :
609  m_pWriter( pWriter ),
610  m_nPageWidth( nPageWidth ),
611  m_nPageHeight( nPageHeight ),
612  m_nUserUnit( 1 ),
613  m_eOrientation( eOrientation ),
614  m_nPageObject( 0 ), // invalid object number
615  m_nStreamLengthObject( 0 ),
616  m_nBeginStreamPos( 0 ),
617  m_eTransition( PDFWriter::PageTransition::Regular ),
618  m_nTransTime( 0 )
619 {
620  // object ref must be only ever updated in emit()
621  m_nPageObject = m_pWriter->createObject();
622 
623  switch (m_pWriter->m_aContext.Version)
624  {
626  m_nUserUnit = std::ceil(std::max(nPageWidth, nPageHeight) / 14400.0);
627  break;
628  default:
629  // 1.2 -> 1.5
630  break;
631  }
632 }
633 
635 {
637  {
638  m_pWriter->emitComment("PDFWriterImpl::PDFPage::beginStream, +");
639  }
640  m_aStreamObjects.push_back(m_pWriter->createObject());
641  if( ! m_pWriter->updateObject( m_aStreamObjects.back() ) )
642  return;
643 
644  m_nStreamLengthObject = m_pWriter->createObject();
645  // write content stream header
646  OStringBuffer aLine;
647  aLine.append( m_aStreamObjects.back() );
648  aLine.append( " 0 obj\n<</Length " );
649  aLine.append( m_nStreamLengthObject );
650  aLine.append( " 0 R" );
652  aLine.append( "/Filter/FlateDecode" );
653  aLine.append( ">>\nstream\n" );
654  if( ! m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() ) )
655  return;
656  if (osl::File::E_None != m_pWriter->m_aFile.getPos(m_nBeginStreamPos))
657  {
658  m_pWriter->m_aFile.close();
659  m_pWriter->m_bOpen = false;
660  }
662  m_pWriter->beginCompression();
663  m_pWriter->checkAndEnableStreamEncryption( m_aStreamObjects.back() );
664 }
665 
667 {
669  m_pWriter->endCompression();
670  sal_uInt64 nEndStreamPos;
671  if (osl::File::E_None != m_pWriter->m_aFile.getPos(nEndStreamPos))
672  {
673  m_pWriter->m_aFile.close();
674  m_pWriter->m_bOpen = false;
675  return;
676  }
677  m_pWriter->disableStreamEncryption();
678  if( ! m_pWriter->writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
679  return;
680  // emit stream length object
681  if( ! m_pWriter->updateObject( m_nStreamLengthObject ) )
682  return;
683  OString aLine =
684  OString::number( m_nStreamLengthObject ) +
685  " 0 obj\n" +
686  OString::number( static_cast<sal_Int64>(nEndStreamPos-m_nBeginStreamPos) ) +
687  "\nendobj\n\n";
688  m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() );
689 }
690 
691 bool PDFPage::emit(sal_Int32 nParentObject )
692 {
693  m_pWriter->MARK("PDFPage::emit");
694  // emit page object
695  if( ! m_pWriter->updateObject( m_nPageObject ) )
696  return false;
697  OStringBuffer aLine;
698 
699  aLine.append( m_nPageObject );
700  aLine.append( " 0 obj\n"
701  "<</Type/Page/Parent " );
702  aLine.append( nParentObject );
703  aLine.append( " 0 R" );
704  aLine.append( "/Resources " );
705  aLine.append( m_pWriter->getResourceDictObj() );
706  aLine.append( " 0 R" );
707  if( m_nPageWidth && m_nPageHeight )
708  {
709  aLine.append( "/MediaBox[0 0 " );
710  aLine.append(m_nPageWidth / m_nUserUnit);
711  aLine.append( ' ' );
712  aLine.append(m_nPageHeight / m_nUserUnit);
713  aLine.append( "]" );
714  if (m_nUserUnit > 1)
715  {
716  aLine.append("\n/UserUnit ");
717  aLine.append(m_nUserUnit);
718  }
719  }
720  switch( m_eOrientation )
721  {
722  case PDFWriter::Orientation::Portrait: aLine.append( "/Rotate 0\n" );break;
724  }
725  int nAnnots = m_aAnnotations.size();
726  if( nAnnots > 0 )
727  {
728  aLine.append( "/Annots[\n" );
729  for( int i = 0; i < nAnnots; i++ )
730  {
731  aLine.append( m_aAnnotations[i] );
732  aLine.append( " 0 R" );
733  aLine.append( ((i+1)%15) ? " " : "\n" );
734  }
735  aLine.append( "]\n" );
736  }
737  if( !m_aMCIDParents.empty() )
738  {
739  OStringBuffer aStructParents( 1024 );
740  aStructParents.append( "[ " );
741  int nParents = m_aMCIDParents.size();
742  for( int i = 0; i < nParents; i++ )
743  {
744  aStructParents.append( m_aMCIDParents[i] );
745  aStructParents.append( " 0 R" );
746  aStructParents.append( ((i%10) == 9) ? "\n" : " " );
747  }
748  aStructParents.append( "]" );
749  m_pWriter->m_aStructParentTree.push_back( aStructParents.makeStringAndClear() );
750 
751  aLine.append( "/StructParents " );
752  aLine.append( sal_Int32(m_pWriter->m_aStructParentTree.size()-1) );
753  aLine.append( "\n" );
754  }
756  {
757  // transition duration
758  aLine.append( "/Trans<</D " );
759  appendDouble( static_cast<double>(m_nTransTime)/1000.0, aLine, 3 );
760  aLine.append( "\n" );
761  const char *pStyle = nullptr, *pDm = nullptr, *pM = nullptr, *pDi = nullptr;
762  switch( m_eTransition )
763  {
765  pStyle = "Split"; pDm = "H"; pM = "I"; break;
767  pStyle = "Split"; pDm = "H"; pM = "O"; break;
769  pStyle = "Split"; pDm = "V"; pM = "I"; break;
771  pStyle = "Split"; pDm = "V"; pM = "O"; break;
773  pStyle = "Blinds"; pDm = "H"; break;
775  pStyle = "Blinds"; pDm = "V"; break;
777  pStyle = "Box"; pM = "I"; break;
779  pStyle = "Box"; pM = "O"; break;
781  pStyle = "Wipe"; pDi = "0"; break;
783  pStyle = "Wipe"; pDi = "90"; break;
785  pStyle = "Wipe"; pDi = "180"; break;
787  pStyle = "Wipe"; pDi = "270"; break;
789  pStyle = "Dissolve"; break;
791  break;
792  }
793  // transition style
794  if( pStyle )
795  {
796  aLine.append( "/S/" );
797  aLine.append( pStyle );
798  aLine.append( "\n" );
799  }
800  if( pDm )
801  {
802  aLine.append( "/Dm/" );
803  aLine.append( pDm );
804  aLine.append( "\n" );
805  }
806  if( pM )
807  {
808  aLine.append( "/M/" );
809  aLine.append( pM );
810  aLine.append( "\n" );
811  }
812  if( pDi )
813  {
814  aLine.append( "/Di " );
815  aLine.append( pDi );
816  aLine.append( "\n" );
817  }
818  aLine.append( ">>\n" );
819  }
820  if( m_pWriter->getVersion() > PDFWriter::PDFVersion::PDF_1_3 && ! m_pWriter->m_bIsPDF_A1 )
821  {
822  aLine.append( "/Group<</S/Transparency/CS/DeviceRGB/I true>>" );
823  }
824  aLine.append( "/Contents" );
825  unsigned int nStreamObjects = m_aStreamObjects.size();
826  if( nStreamObjects > 1 )
827  aLine.append( '[' );
828  for(sal_Int32 i : m_aStreamObjects)
829  {
830  aLine.append( ' ' );
831  aLine.append( i );
832  aLine.append( " 0 R" );
833  }
834  if( nStreamObjects > 1 )
835  aLine.append( ']' );
836  aLine.append( ">>\nendobj\n\n" );
837  return m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() );
838 }
839 
840 void PDFPage::appendPoint( const Point& rPoint, OStringBuffer& rBuffer ) const
841 {
842  Point aPoint( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
843  m_pWriter->m_aMapMode,
844  m_pWriter,
845  rPoint ) );
846 
847  sal_Int32 nValue = aPoint.X();
848 
849  appendFixedInt( nValue, rBuffer );
850 
851  rBuffer.append( ' ' );
852 
853  nValue = pointToPixel(getHeight()) - aPoint.Y();
854 
855  appendFixedInt( nValue, rBuffer );
856 }
857 
858 void PDFPage::appendPixelPoint( const basegfx::B2DPoint& rPoint, OStringBuffer& rBuffer ) const
859 {
860  double fValue = pixelToPoint(rPoint.getX());
861 
862  appendDouble( fValue, rBuffer, nLog10Divisor );
863  rBuffer.append( ' ' );
864  fValue = getHeight() - pixelToPoint(rPoint.getY());
865  appendDouble( fValue, rBuffer, nLog10Divisor );
866 }
867 
868 void PDFPage::appendRect( const tools::Rectangle& rRect, OStringBuffer& rBuffer ) const
869 {
870  appendPoint( rRect.BottomLeft() + Point( 0, 1 ), rBuffer );
871  rBuffer.append( ' ' );
872  appendMappedLength( static_cast<sal_Int32>(rRect.GetWidth()), rBuffer, false );
873  rBuffer.append( ' ' );
874  appendMappedLength( static_cast<sal_Int32>(rRect.GetHeight()), rBuffer );
875  rBuffer.append( " re" );
876 }
877 
879 {
880  Point aLL = lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
881  m_pWriter->m_aMapMode,
882  m_pWriter,
883  rRect.BottomLeft() + Point( 0, 1 )
884  );
885  Size aSize = lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
886  m_pWriter->m_aMapMode,
887  m_pWriter,
888  rRect.GetSize() );
889  rRect.SetLeft( aLL.X() );
890  rRect.SetRight( aLL.X() + aSize.Width() );
891  rRect.SetTop( pointToPixel(getHeight()) - aLL.Y() );
892  rRect.SetBottom( rRect.Top() + aSize.Height() );
893 }
894 
895 void PDFPage::appendPolygon( const tools::Polygon& rPoly, OStringBuffer& rBuffer, bool bClose ) const
896 {
897  sal_uInt16 nPoints = rPoly.GetSize();
898  /*
899  * #108582# applications do weird things
900  */
901  sal_uInt32 nBufLen = rBuffer.getLength();
902  if( nPoints > 0 )
903  {
904  const PolyFlags* pFlagArray = rPoly.GetConstFlagAry();
905  appendPoint( rPoly[0], rBuffer );
906  rBuffer.append( " m\n" );
907  for( sal_uInt16 i = 1; i < nPoints; i++ )
908  {
909  if( pFlagArray && pFlagArray[i] == PolyFlags::Control && nPoints-i > 2 )
910  {
911  // bezier
912  SAL_WARN_IF( pFlagArray[i+1] != PolyFlags::Control || pFlagArray[i+2] == PolyFlags::Control, "vcl.pdfwriter", "unexpected sequence of control points" );
913  appendPoint( rPoly[i], rBuffer );
914  rBuffer.append( " " );
915  appendPoint( rPoly[i+1], rBuffer );
916  rBuffer.append( " " );
917  appendPoint( rPoly[i+2], rBuffer );
918  rBuffer.append( " c" );
919  i += 2; // add additionally consumed points
920  }
921  else
922  {
923  // line
924  appendPoint( rPoly[i], rBuffer );
925  rBuffer.append( " l" );
926  }
927  if( (rBuffer.getLength() - nBufLen) > 65 )
928  {
929  rBuffer.append( "\n" );
930  nBufLen = rBuffer.getLength();
931  }
932  else
933  rBuffer.append( " " );
934  }
935  if( bClose )
936  rBuffer.append( "h\n" );
937  }
938 }
939 
940 void PDFPage::appendPolygon( const basegfx::B2DPolygon& rPoly, OStringBuffer& rBuffer ) const
941 {
942  basegfx::B2DPolygon aPoly( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
943  m_pWriter->m_aMapMode,
944  m_pWriter,
945  rPoly ) );
946 
947  if( basegfx::utils::isRectangle( aPoly ) )
948  {
949  basegfx::B2DRange aRange( aPoly.getB2DRange() );
950  basegfx::B2DPoint aBL( aRange.getMinX(), aRange.getMaxY() );
951  appendPixelPoint( aBL, rBuffer );
952  rBuffer.append( ' ' );
953  appendMappedLength( aRange.getWidth(), rBuffer, false, nLog10Divisor );
954  rBuffer.append( ' ' );
955  appendMappedLength( aRange.getHeight(), rBuffer, true, nLog10Divisor );
956  rBuffer.append( " re\n" );
957  return;
958  }
959  sal_uInt32 nPoints = aPoly.count();
960  if( nPoints > 0 )
961  {
962  sal_uInt32 nBufLen = rBuffer.getLength();
963  basegfx::B2DPoint aLastPoint( aPoly.getB2DPoint( 0 ) );
964  appendPixelPoint( aLastPoint, rBuffer );
965  rBuffer.append( " m\n" );
966  for( sal_uInt32 i = 1; i <= nPoints; i++ )
967  {
968  if( i != nPoints || aPoly.isClosed() )
969  {
970  sal_uInt32 nCurPoint = i % nPoints;
971  sal_uInt32 nLastPoint = i-1;
972  basegfx::B2DPoint aPoint( aPoly.getB2DPoint( nCurPoint ) );
973  if( aPoly.isNextControlPointUsed( nLastPoint ) &&
974  aPoly.isPrevControlPointUsed( nCurPoint ) )
975  {
976  appendPixelPoint( aPoly.getNextControlPoint( nLastPoint ), rBuffer );
977  rBuffer.append( ' ' );
978  appendPixelPoint( aPoly.getPrevControlPoint( nCurPoint ), rBuffer );
979  rBuffer.append( ' ' );
980  appendPixelPoint( aPoint, rBuffer );
981  rBuffer.append( " c" );
982  }
983  else if( aPoly.isNextControlPointUsed( nLastPoint ) )
984  {
985  appendPixelPoint( aPoly.getNextControlPoint( nLastPoint ), rBuffer );
986  rBuffer.append( ' ' );
987  appendPixelPoint( aPoint, rBuffer );
988  rBuffer.append( " y" );
989  }
990  else if( aPoly.isPrevControlPointUsed( nCurPoint ) )
991  {
992  appendPixelPoint( aPoly.getPrevControlPoint( nCurPoint ), rBuffer );
993  rBuffer.append( ' ' );
994  appendPixelPoint( aPoint, rBuffer );
995  rBuffer.append( " v" );
996  }
997  else
998  {
999  appendPixelPoint( aPoint, rBuffer );
1000  rBuffer.append( " l" );
1001  }
1002  if( (rBuffer.getLength() - nBufLen) > 65 )
1003  {
1004  rBuffer.append( "\n" );
1005  nBufLen = rBuffer.getLength();
1006  }
1007  else
1008  rBuffer.append( " " );
1009  }
1010  }
1011  rBuffer.append( "h\n" );
1012  }
1013 }
1014 
1015 void PDFPage::appendPolyPolygon( const tools::PolyPolygon& rPolyPoly, OStringBuffer& rBuffer ) const
1016 {
1017  sal_uInt16 nPolygons = rPolyPoly.Count();
1018  for( sal_uInt16 n = 0; n < nPolygons; n++ )
1019  appendPolygon( rPolyPoly[n], rBuffer );
1020 }
1021 
1022 void PDFPage::appendPolyPolygon( const basegfx::B2DPolyPolygon& rPolyPoly, OStringBuffer& rBuffer ) const
1023 {
1024  for(auto const& rPolygon : rPolyPoly)
1025  appendPolygon( rPolygon, rBuffer );
1026 }
1027 
1028 void PDFPage::appendMappedLength( sal_Int32 nLength, OStringBuffer& rBuffer, bool bVertical, sal_Int32* pOutLength ) const
1029 {
1030  sal_Int32 nValue = nLength;
1031  if ( nLength < 0 )
1032  {
1033  rBuffer.append( '-' );
1034  nValue = -nLength;
1035  }
1036  Size aSize( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1037  m_pWriter->m_aMapMode,
1038  m_pWriter,
1039  Size( nValue, nValue ) ) );
1040  nValue = bVertical ? aSize.Height() : aSize.Width();
1041  if( pOutLength )
1042  *pOutLength = ((nLength < 0 ) ? -nValue : nValue);
1043 
1044  appendFixedInt( nValue, rBuffer );
1045 }
1046 
1047 void PDFPage::appendMappedLength( double fLength, OStringBuffer& rBuffer, bool bVertical, sal_Int32 nPrecision ) const
1048 {
1049  Size aSize( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1050  m_pWriter->m_aMapMode,
1051  m_pWriter,
1052  Size( 1000, 1000 ) ) );
1053  fLength *= pixelToPoint(static_cast<double>(bVertical ? aSize.Height() : aSize.Width()) / 1000.0);
1054  appendDouble( fLength, rBuffer, nPrecision );
1055 }
1056 
1057 bool PDFPage::appendLineInfo( const LineInfo& rInfo, OStringBuffer& rBuffer ) const
1058 {
1059  if(LineStyle::Dash == rInfo.GetStyle() && rInfo.GetDashLen() != rInfo.GetDotLen())
1060  {
1061  // dashed and non-degraded case, check for implementation limits of dash array
1062  // in PDF reader apps (e.g. acroread)
1063  if(2 * (rInfo.GetDashCount() + rInfo.GetDotCount()) > 10)
1064  {
1065  return false;
1066  }
1067  }
1068 
1069  if(basegfx::B2DLineJoin::NONE != rInfo.GetLineJoin())
1070  {
1071  // LineJoin used, ExtLineInfo required
1072  return false;
1073  }
1074 
1075  if(css::drawing::LineCap_BUTT != rInfo.GetLineCap())
1076  {
1077  // LineCap used, ExtLineInfo required
1078  return false;
1079  }
1080 
1081  if( rInfo.GetStyle() == LineStyle::Dash )
1082  {
1083  rBuffer.append( "[ " );
1084  if( rInfo.GetDashLen() == rInfo.GetDotLen() ) // degraded case
1085  {
1086  appendMappedLength( rInfo.GetDashLen(), rBuffer );
1087  rBuffer.append( ' ' );
1088  appendMappedLength( rInfo.GetDistance(), rBuffer );
1089  rBuffer.append( ' ' );
1090  }
1091  else
1092  {
1093  for( int n = 0; n < rInfo.GetDashCount(); n++ )
1094  {
1095  appendMappedLength( rInfo.GetDashLen(), rBuffer );
1096  rBuffer.append( ' ' );
1097  appendMappedLength( rInfo.GetDistance(), rBuffer );
1098  rBuffer.append( ' ' );
1099  }
1100  for( int m = 0; m < rInfo.GetDotCount(); m++ )
1101  {
1102  appendMappedLength( rInfo.GetDotLen(), rBuffer );
1103  rBuffer.append( ' ' );
1104  appendMappedLength( rInfo.GetDistance(), rBuffer );
1105  rBuffer.append( ' ' );
1106  }
1107  }
1108  rBuffer.append( "] 0 d\n" );
1109  }
1110 
1111  if( rInfo.GetWidth() > 1 )
1112  {
1113  appendMappedLength( rInfo.GetWidth(), rBuffer );
1114  rBuffer.append( " w\n" );
1115  }
1116  else if( rInfo.GetWidth() == 0 )
1117  {
1118  // "pixel" line
1119  appendDouble( 72.0/double(m_pWriter->GetDPIX()), rBuffer );
1120  rBuffer.append( " w\n" );
1121  }
1122 
1123  return true;
1124 }
1125 
1126 void PDFPage::appendWaveLine( sal_Int32 nWidth, sal_Int32 nY, sal_Int32 nDelta, OStringBuffer& rBuffer ) const
1127 {
1128  if( nWidth <= 0 )
1129  return;
1130  if( nDelta < 1 )
1131  nDelta = 1;
1132 
1133  rBuffer.append( "0 " );
1134  appendMappedLength( nY, rBuffer );
1135  rBuffer.append( " m\n" );
1136  for( sal_Int32 n = 0; n < nWidth; )
1137  {
1138  n += nDelta;
1139  appendMappedLength( n, rBuffer, false );
1140  rBuffer.append( ' ' );
1141  appendMappedLength( nDelta+nY, rBuffer );
1142  rBuffer.append( ' ' );
1143  n += nDelta;
1144  appendMappedLength( n, rBuffer, false );
1145  rBuffer.append( ' ' );
1146  appendMappedLength( nY, rBuffer );
1147  rBuffer.append( " v " );
1148  if( n < nWidth )
1149  {
1150  n += nDelta;
1151  appendMappedLength( n, rBuffer, false );
1152  rBuffer.append( ' ' );
1153  appendMappedLength( nY-nDelta, rBuffer );
1154  rBuffer.append( ' ' );
1155  n += nDelta;
1156  appendMappedLength( n, rBuffer, false );
1157  rBuffer.append( ' ' );
1158  appendMappedLength( nY, rBuffer );
1159  rBuffer.append( " v\n" );
1160  }
1161  }
1162  rBuffer.append( "S\n" );
1163 }
1164 
1165 void PDFPage::appendMatrix3(Matrix3 const & rMatrix, OStringBuffer& rBuffer)
1166 {
1167  appendDouble(rMatrix.get(0), rBuffer);
1168  rBuffer.append(' ');
1169  appendDouble(rMatrix.get(1), rBuffer);
1170  rBuffer.append(' ');
1171  appendDouble(rMatrix.get(2), rBuffer);
1172  rBuffer.append(' ');
1173  appendDouble(rMatrix.get(3), rBuffer);
1174  rBuffer.append(' ');
1175  appendPoint(Point(long(rMatrix.get(4)), long(rMatrix.get(5))), rBuffer);
1176 }
1177 
1178 double PDFPage::getHeight() const
1179 {
1181 
1182  if (m_nUserUnit > 1)
1183  {
1184  fRet /= m_nUserUnit;
1185  }
1186 
1187  return fRet;
1188 }
1189 
1191  const css::uno::Reference< css::beans::XMaterialHolder >& xEnc,
1192  PDFWriter& i_rOuterFace)
1194  m_aMapMode( MapUnit::MapPoint, Point(), Fraction( 1, pointToPixel(1) ), Fraction( 1, pointToPixel(1) ) ),
1195  m_nCurrentStructElement( 0 ),
1196  m_bEmitStructure( true ),
1197  m_nNextFID( 1 ),
1198  m_aPDFBmpCache(
1199  officecfg::Office::Common::VCL::PDFExportImageCacheSize::get() ),
1200  m_nCurrentPage( -1 ),
1201  m_nCatalogObject(0),
1202  m_nSignatureObject( -1 ),
1203  m_nSignatureContentOffset( 0 ),
1204  m_nSignatureLastByteRangeNoOffset( 0 ),
1205  m_nResourceDict( -1 ),
1206  m_nFontDictObject( -1 ),
1207  m_aContext(rContext),
1208  m_aFile(m_aContext.URL),
1209  m_bOpen(false),
1210  m_DocDigest(::comphelper::HashType::MD5),
1211  m_aCipher( nullptr ),
1212  m_nKeyLength(0),
1213  m_nRC4KeyLength(0),
1214  m_bEncryptThisStream( false ),
1215  m_nAccessPermissions(0),
1216  m_bIsPDF_A1( false ),
1217  m_bIsPDF_A2( false ),
1218  m_bIsPDF_UA( false ),
1219  m_bIsPDF_A3( false ),
1220  m_rOuterFace( i_rOuterFace )
1221 {
1222  m_aStructure.emplace_back( );
1223  m_aStructure[0].m_nOwnElement = 0;
1224  m_aStructure[0].m_nParentElement = 0;
1225 
1226  Font aFont;
1227  aFont.SetFamilyName( "Times" );
1228  aFont.SetFontSize( Size( 0, 12 ) );
1229 
1230  GraphicsState aState;
1231  aState.m_aMapMode = m_aMapMode;
1232  aState.m_aFont = aFont;
1233  m_aGraphicsStack.push_front( aState );
1234 
1235  osl::File::RC aError = m_aFile.open(osl_File_OpenFlag_Write | osl_File_OpenFlag_Create);
1236  if (aError != osl::File::E_None)
1237  {
1238  if (aError == osl::File::E_EXIST)
1239  {
1240  aError = m_aFile.open(osl_File_OpenFlag_Write);
1241  if (aError == osl::File::E_None)
1242  aError = m_aFile.setSize(0);
1243  }
1244  }
1245  if (aError != osl::File::E_None)
1246  return;
1247 
1248  m_bOpen = true;
1249 
1250  // setup DocInfo
1251  setupDocInfo();
1252 
1253  /* prepare the cypher engine, can be done in CTOR, free in DTOR */
1254  m_aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream );
1255 
1256  if( xEnc.is() )
1257  prepareEncryption( xEnc );
1258 
1259  if( m_aContext.Encryption.Encrypt() )
1260  {
1261  // sanity check
1265  )
1266  {
1267  // the field lengths are invalid ? This was not setup by initEncryption.
1268  // do not encrypt after all
1269  m_aContext.Encryption.OValue.clear();
1270  m_aContext.Encryption.UValue.clear();
1271  OSL_ENSURE( false, "encryption data failed sanity check, encryption disabled" );
1272  }
1273  else // setup key lengths
1275  }
1276 
1277  // write header
1278  OStringBuffer aBuffer( 20 );
1279  aBuffer.append( "%PDF-" );
1280  switch( m_aContext.Version )
1281  {
1282  case PDFWriter::PDFVersion::PDF_1_2: aBuffer.append( "1.2" );break;
1283  case PDFWriter::PDFVersion::PDF_1_3: aBuffer.append( "1.3" );break;
1285  case PDFWriter::PDFVersion::PDF_1_4: aBuffer.append( "1.4" );break;
1286  case PDFWriter::PDFVersion::PDF_1_5: aBuffer.append( "1.5" );break;
1287  default:
1288  case PDFWriter::PDFVersion::PDF_1_6: aBuffer.append( "1.6" );break;
1289  }
1290  // append something binary as comment (suggested in PDF Reference)
1291  aBuffer.append( "\n%\303\244\303\274\303\266\303\237\n" );
1292  if( !writeBuffer( aBuffer.getStr(), aBuffer.getLength() ) )
1293  {
1294  m_aFile.close();
1295  m_bOpen = false;
1296  return;
1297  }
1298 
1299  // insert outline root
1300  m_aOutline.emplace_back( );
1301 
1303  if( m_bIsPDF_A1 )
1304  m_aContext.Version = PDFWriter::PDFVersion::PDF_1_4; //meaning we need PDF 1.4, PDF/A flavour
1305 
1307  if( m_bIsPDF_A2 )
1308  m_aContext.Version = PDFWriter::PDFVersion::PDF_1_6; //we could even use 1.7 features
1309 
1311  if( m_bIsPDF_A3 )
1312  m_aContext.Version = PDFWriter::PDFVersion::PDF_1_6; //we could even use 1.7 features
1313 
1315  {
1316  m_bIsPDF_UA = true;
1317  m_aContext.Tagged = true;
1318  }
1319 
1320  if( m_aContext.DPIx == 0 || m_aContext.DPIy == 0 )
1322  else
1324 
1325  SetOutputSizePixel( Size( 640, 480 ) );
1326  SetMapMode(MapMode(MapUnit::MapMM));
1327 }
1328 
1330 {
1331  disposeOnce();
1332 }
1333 
1335 {
1336  if( m_aCipher )
1337  rtl_cipher_destroyARCFOUR( m_aCipher );
1338  m_aPages.clear();
1340 }
1341 
1343 {
1344  std::vector< sal_uInt8 > aId;
1349 }
1350 
1352 {
1353  OStringBuffer aRet;
1354 
1355  TimeValue aTVal, aGMT;
1356  oslDateTime aDT;
1357  osl_getSystemTime(&aGMT);
1358  osl_getLocalTimeFromSystemTime(&aGMT, &aTVal);
1359  osl_getDateTimeFromTimeValue(&aTVal, &aDT);
1360 
1361  sal_uInt32 nDelta = 0;
1362  if (aGMT.Seconds > aTVal.Seconds)
1363  {
1364  nDelta = aGMT.Seconds-aTVal.Seconds;
1365  }
1366  else if (aGMT.Seconds < aTVal.Seconds)
1367  {
1368  nDelta = aTVal.Seconds-aGMT.Seconds;
1369  }
1370 
1371  appendPdfTimeDate(aRet, aDT.Year, aDT.Month, aDT.Day, aDT.Hours, aDT.Minutes, aDT.Seconds, nDelta);
1372 
1373  aRet.append("'");
1374  return aRet.makeStringAndClear();
1375 }
1376 
1377 void PDFWriterImpl::computeDocumentIdentifier( std::vector< sal_uInt8 >& o_rIdentifier,
1378  const vcl::PDFWriter::PDFDocInfo& i_rDocInfo,
1379  const OString& i_rCString1,
1380  OString& o_rCString2
1381  )
1382 {
1383  o_rIdentifier.clear();
1384 
1385  //build the document id
1386  OString aInfoValuesOut;
1387  OStringBuffer aID( 1024 );
1388  if( !i_rDocInfo.Title.isEmpty() )
1389  PDFWriter::AppendUnicodeTextString(i_rDocInfo.Title, aID);
1390  if( !i_rDocInfo.Author.isEmpty() )
1391  PDFWriter::AppendUnicodeTextString(i_rDocInfo.Author, aID);
1392  if( !i_rDocInfo.Subject.isEmpty() )
1394  if( !i_rDocInfo.Keywords.isEmpty() )
1396  if( !i_rDocInfo.Creator.isEmpty() )
1398  if( !i_rDocInfo.Producer.isEmpty() )
1400 
1401  TimeValue aTVal, aGMT;
1402  oslDateTime aDT;
1403  osl_getSystemTime( &aGMT );
1404  osl_getLocalTimeFromSystemTime( &aGMT, &aTVal );
1405  osl_getDateTimeFromTimeValue( &aTVal, &aDT );
1406  OStringBuffer aCreationMetaDateString(64);
1407 
1408  // i59651: we fill the Metadata date string as well, if PDF/A is requested
1409  // according to ISO 19005-1:2005 6.7.3 the date is corrected for
1410  // local time zone offset UTC only, whereas Acrobat 8 seems
1411  // to use the localtime notation only
1412  // according to a recommendation in XMP Specification (Jan 2004, page 75)
1413  // the Acrobat way seems the right approach
1414  aCreationMetaDateString.append( static_cast<char>('0' + ((aDT.Year/1000)%10)) );
1415  aCreationMetaDateString.append( static_cast<char>('0' + ((aDT.Year/100)%10)) );
1416  aCreationMetaDateString.append( static_cast<char>('0' + ((aDT.Year/10)%10)) );
1417  aCreationMetaDateString.append( static_cast<char>('0' + ((aDT.Year)%10)) );
1418  aCreationMetaDateString.append( "-" );
1419  aCreationMetaDateString.append( static_cast<char>('0' + ((aDT.Month/10)%10)) );
1420  aCreationMetaDateString.append( static_cast<char>('0' + ((aDT.Month)%10)) );
1421  aCreationMetaDateString.append( "-" );
1422  aCreationMetaDateString.append( static_cast<char>('0' + ((aDT.Day/10)%10)) );
1423  aCreationMetaDateString.append( static_cast<char>('0' + ((aDT.Day)%10)) );
1424  aCreationMetaDateString.append( "T" );
1425  aCreationMetaDateString.append( static_cast<char>('0' + ((aDT.Hours/10)%10)) );
1426  aCreationMetaDateString.append( static_cast<char>('0' + ((aDT.Hours)%10)) );
1427  aCreationMetaDateString.append( ":" );
1428  aCreationMetaDateString.append( static_cast<char>('0' + ((aDT.Minutes/10)%10)) );
1429  aCreationMetaDateString.append( static_cast<char>('0' + ((aDT.Minutes)%10)) );
1430  aCreationMetaDateString.append( ":" );
1431  aCreationMetaDateString.append( static_cast<char>('0' + ((aDT.Seconds/10)%10)) );
1432  aCreationMetaDateString.append( static_cast<char>('0' + ((aDT.Seconds)%10)) );
1433 
1434  sal_uInt32 nDelta = 0;
1435  if( aGMT.Seconds > aTVal.Seconds )
1436  {
1437  nDelta = aGMT.Seconds-aTVal.Seconds;
1438  aCreationMetaDateString.append( "-" );
1439  }
1440  else if( aGMT.Seconds < aTVal.Seconds )
1441  {
1442  nDelta = aTVal.Seconds-aGMT.Seconds;
1443  aCreationMetaDateString.append( "+" );
1444  }
1445  else
1446  {
1447  aCreationMetaDateString.append( "Z" );
1448 
1449  }
1450  if( nDelta )
1451  {
1452  aCreationMetaDateString.append( static_cast<char>('0' + ((nDelta/36000)%10)) );
1453  aCreationMetaDateString.append( static_cast<char>('0' + ((nDelta/3600)%10)) );
1454  aCreationMetaDateString.append( ":" );
1455  aCreationMetaDateString.append( static_cast<char>('0' + ((nDelta/600)%6)) );
1456  aCreationMetaDateString.append( static_cast<char>('0' + ((nDelta/60)%10)) );
1457  }
1458  aID.append( i_rCString1.getStr(), i_rCString1.getLength() );
1459 
1460  aInfoValuesOut = aID.makeStringAndClear();
1461  o_rCString2 = aCreationMetaDateString.makeStringAndClear();
1462 
1464  aDigest.update(reinterpret_cast<unsigned char const*>(&aGMT), sizeof(aGMT));
1465  aDigest.update(reinterpret_cast<unsigned char const*>(aInfoValuesOut.getStr()), aInfoValuesOut.getLength());
1466  //the binary form of the doc id is needed for encryption stuff
1467  o_rIdentifier = aDigest.finalize();
1468 }
1469 
1470 /* i12626 methods */
1471 /*
1472 check if the Unicode string must be encrypted or not, perform the requested task,
1473 append the string as unicode hex, encrypted if needed
1474  */
1475 inline void PDFWriterImpl::appendUnicodeTextStringEncrypt( const OUString& rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer )
1476 {
1477  rOutBuffer.append( "<" );
1478  if( m_aContext.Encryption.Encrypt() )
1479  {
1480  const sal_Unicode* pStr = rInString.getStr();
1481  sal_Int32 nLen = rInString.getLength();
1482  //prepare a unicode string, encrypt it
1483  enableStringEncryption( nInObjectNumber );
1484  sal_uInt8 *pCopy = m_vEncryptionBuffer.data();
1485  sal_Int32 nChars = 2 + (nLen * 2);
1486  m_vEncryptionBuffer.resize(nChars);
1487  *pCopy++ = 0xFE;
1488  *pCopy++ = 0xFF;
1489  // we need to prepare a byte stream from the unicode string buffer
1490  for( int i = 0; i < nLen; i++ )
1491  {
1492  sal_Unicode aUnChar = pStr[i];
1493  *pCopy++ = static_cast<sal_uInt8>( aUnChar >> 8 );
1494  *pCopy++ = static_cast<sal_uInt8>( aUnChar & 255 );
1495  }
1496  //encrypt in place
1497  rtl_cipher_encodeARCFOUR( m_aCipher, m_vEncryptionBuffer.data(), nChars, m_vEncryptionBuffer.data(), nChars );
1498  //now append, hexadecimal (appendHex), the encrypted result
1499  for(int i = 0; i < nChars; i++)
1500  appendHex( m_vEncryptionBuffer[i], rOutBuffer );
1501  }
1502  else
1503  PDFWriter::AppendUnicodeTextString(rInString, rOutBuffer);
1504  rOutBuffer.append( ">" );
1505 }
1506 
1507 inline void PDFWriterImpl::appendLiteralStringEncrypt( OStringBuffer const & rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer )
1508 {
1509  rOutBuffer.append( "(" );
1510  sal_Int32 nChars = rInString.getLength();
1511  //check for encryption, if ok, encrypt the string, then convert with appndLiteralString
1512  if( m_aContext.Encryption.Encrypt() )
1513  {
1514  m_vEncryptionBuffer.resize(nChars);
1515  //encrypt the string in a buffer, then append it
1516  enableStringEncryption( nInObjectNumber );
1517  rtl_cipher_encodeARCFOUR( m_aCipher, rInString.getStr(), nChars, m_vEncryptionBuffer.data(), nChars );
1518  appendLiteralString( reinterpret_cast<char*>(m_vEncryptionBuffer.data()), nChars, rOutBuffer );
1519  }
1520  else
1521  appendLiteralString( rInString.getStr(), nChars , rOutBuffer );
1522  rOutBuffer.append( ")" );
1523 }
1524 
1525 inline void PDFWriterImpl::appendLiteralStringEncrypt( const OString& rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer )
1526 {
1527  OStringBuffer aBufferString( rInString );
1528  appendLiteralStringEncrypt( aBufferString, nInObjectNumber, rOutBuffer);
1529 }
1530 
1531 void PDFWriterImpl::appendLiteralStringEncrypt( const OUString& rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer, rtl_TextEncoding nEnc )
1532 {
1533  OString aBufferString( OUStringToOString( rInString, nEnc ) );
1534  sal_Int32 nLen = aBufferString.getLength();
1535  OStringBuffer aBuf( nLen );
1536  const char* pT = aBufferString.getStr();
1537 
1538  for( sal_Int32 i = 0; i < nLen; i++, pT++ )
1539  {
1540  if( (*pT & 0x80) == 0 )
1541  aBuf.append( *pT );
1542  else
1543  {
1544  aBuf.append( '<' );
1545  appendHex( *pT, aBuf );
1546  aBuf.append( '>' );
1547  }
1548  }
1549  aBufferString = aBuf.makeStringAndClear();
1550  appendLiteralStringEncrypt( aBufferString, nInObjectNumber, rOutBuffer);
1551 }
1552 
1553 /* end i12626 methods */
1554 
1555 void PDFWriterImpl::emitComment( const char* pComment )
1556 {
1557  OString aLine = "% " + rtl::OStringView(pComment) + "\n";
1558  writeBuffer( aLine.getStr(), aLine.getLength() );
1559 }
1560 
1562 {
1564  {
1565  sal_uLong nEndPos = pStream->TellEnd();
1566  pStream->Seek( STREAM_SEEK_TO_BEGIN );
1567  ZCodec aCodec( 0x4000, 0x4000 );
1568  SvMemoryStream aStream;
1569  aCodec.BeginCompression();
1570  aCodec.Write( aStream, static_cast<const sal_uInt8*>(pStream->GetData()), nEndPos );
1571  aCodec.EndCompression();
1572  nEndPos = aStream.Tell();
1573  pStream->Seek( STREAM_SEEK_TO_BEGIN );
1574  aStream.Seek( STREAM_SEEK_TO_BEGIN );
1575  pStream->SetStreamSize( nEndPos );
1576  pStream->WriteBytes( aStream.GetData(), nEndPos );
1577  return true;
1578  }
1579  else
1580  return false;
1581 }
1582 
1584 {
1586  {
1587  m_pCodec = std::make_unique<ZCodec>( 0x4000, 0x4000 );
1588  m_pMemStream = std::make_unique<SvMemoryStream>();
1589  m_pCodec->BeginCompression();
1590  }
1591 }
1592 
1594 {
1596  {
1597  m_pCodec->EndCompression();
1598  m_pCodec.reset();
1599  sal_uInt64 nLen = m_pMemStream->Tell();
1600  m_pMemStream->Seek( 0 );
1601  writeBuffer( m_pMemStream->GetData(), nLen );
1602  m_pMemStream.reset();
1603  }
1604 }
1605 
1606 bool PDFWriterImpl::writeBuffer( const void* pBuffer, sal_uInt64 nBytes )
1607 {
1608  if( ! m_bOpen ) // we are already down the drain
1609  return false;
1610 
1611  if( ! nBytes ) // huh ?
1612  return true;
1613 
1614  if( !m_aOutputStreams.empty() )
1615  {
1616  m_aOutputStreams.front().m_pStream->Seek( STREAM_SEEK_TO_END );
1617  m_aOutputStreams.front().m_pStream->WriteBytes(
1618  pBuffer, sal::static_int_cast<std::size_t>(nBytes));
1619  return true;
1620  }
1621 
1622  sal_uInt64 nWritten;
1623  if( m_pCodec )
1624  {
1625  m_pCodec->Write( *m_pMemStream, static_cast<const sal_uInt8*>(pBuffer), static_cast<sal_uLong>(nBytes) );
1626  nWritten = nBytes;
1627  }
1628  else
1629  {
1630  bool buffOK = true;
1631  if( m_bEncryptThisStream )
1632  {
1633  /* implement the encryption part of the PDF spec encryption algorithm 3.1 */
1634  m_vEncryptionBuffer.resize(nBytes);
1635  if( buffOK )
1636  rtl_cipher_encodeARCFOUR( m_aCipher,
1637  pBuffer, static_cast<sal_Size>(nBytes),
1638  m_vEncryptionBuffer.data(), static_cast<sal_Size>(nBytes) );
1639  }
1640 
1641  const void* pWriteBuffer = ( m_bEncryptThisStream && buffOK ) ? m_vEncryptionBuffer.data() : pBuffer;
1642  m_DocDigest.update(static_cast<unsigned char const*>(pWriteBuffer), static_cast<sal_uInt32>(nBytes));
1643 
1644  if (m_aFile.write(pWriteBuffer, nBytes, nWritten) != osl::File::E_None)
1645  nWritten = 0;
1646 
1647  if( nWritten != nBytes )
1648  {
1649  m_aFile.close();
1650  m_bOpen = false;
1651  }
1652  }
1653 
1654  return nWritten == nBytes;
1655 }
1656 
1657 void PDFWriterImpl::newPage( double nPageWidth, double nPageHeight, PDFWriter::Orientation eOrientation )
1658 {
1659  endPage();
1660  m_nCurrentPage = m_aPages.size();
1661  m_aPages.emplace_back(this, nPageWidth, nPageHeight, eOrientation );
1662 
1663  sal_Int32 nUserUnit = m_aPages.back().m_nUserUnit;
1664  if (nUserUnit > 1)
1665  {
1666  m_aMapMode = MapMode(MapUnit::MapPoint, Point(), Fraction(nUserUnit, pointToPixel(1)),
1667  Fraction(nUserUnit, pointToPixel(1)));
1668  }
1669 
1670  m_aPages.back().beginStream();
1671 
1672  // setup global graphics state
1673  // linewidth is "1 pixel" by default
1674  OStringBuffer aBuf( 16 );
1675  appendDouble( 72.0/double(GetDPIX()), aBuf );
1676  aBuf.append( " w\n" );
1677  writeBuffer( aBuf.getStr(), aBuf.getLength() );
1678 }
1679 
1681 {
1682  if( m_aPages.empty() )
1683  return;
1684 
1685  // close eventual MC sequence
1687 
1688  // sanity check
1689  if( !m_aOutputStreams.empty() )
1690  {
1691  OSL_FAIL( "redirection across pages !!!" );
1692  m_aOutputStreams.clear(); // leak !
1693  m_aMapMode.SetOrigin( Point() );
1694  }
1695 
1696  m_aGraphicsStack.clear();
1697  m_aGraphicsStack.emplace_back( );
1698 
1699  // this should pop the PDF graphics stack if necessary
1701 
1702  m_aPages.back().endStream();
1703 
1704  // reset the default font
1705  Font aFont;
1706  aFont.SetFamilyName( "Times" );
1707  aFont.SetFontSize( Size( 0, 12 ) );
1708 
1710  m_aGraphicsStack.front().m_aFont = aFont;
1711 
1712  for (auto & bitmap : m_aBitmaps)
1713  {
1714  if( ! bitmap.m_aBitmap.IsEmpty() )
1715  {
1716  writeBitmapObject(bitmap);
1717  bitmap.m_aBitmap = BitmapEx();
1718  }
1719  }
1720  for (auto & jpeg : m_aJPGs)
1721  {
1722  if( jpeg.m_pStream )
1723  {
1724  writeJPG( jpeg );
1725  jpeg.m_pStream.reset();
1726  jpeg.m_aMask = Bitmap();
1727  }
1728  }
1729  for (auto & item : m_aTransparentObjects)
1730  {
1731  if( item.m_pContentStream )
1732  {
1733  writeTransparentObject(item);
1734  item.m_pContentStream.reset();
1735  }
1736  }
1737 
1738 }
1739 
1741 {
1742  m_aObjects.push_back( ~0U );
1743  return m_aObjects.size();
1744 }
1745 
1746 bool PDFWriterImpl::updateObject( sal_Int32 n )
1747 {
1748  if( ! m_bOpen )
1749  return false;
1750 
1751  sal_uInt64 nOffset = ~0U;
1752  osl::File::RC aError = m_aFile.getPos(nOffset);
1753  SAL_WARN_IF( aError != osl::File::E_None, "vcl.pdfwriter", "could not register object" );
1754  if (aError != osl::File::E_None)
1755  {
1756  m_aFile.close();
1757  m_bOpen = false;
1758  }
1759  m_aObjects[ n-1 ] = nOffset;
1760  return aError == osl::File::E_None;
1761 }
1762 
1763 #define CHECK_RETURN( x ) if( !(x) ) return 0
1764 #define CHECK_RETURN2( x ) if( !(x) ) return
1765 
1766 sal_Int32 PDFWriterImpl::emitStructParentTree( sal_Int32 nObject )
1767 {
1768  if( nObject > 0 )
1769  {
1770  OStringBuffer aLine( 1024 );
1771 
1772  aLine.append( nObject );
1773  aLine.append( " 0 obj\n"
1774  "<</Nums[\n" );
1775  sal_Int32 nTreeItems = m_aStructParentTree.size();
1776  for( sal_Int32 n = 0; n < nTreeItems; n++ )
1777  {
1778  aLine.append( n );
1779  aLine.append( ' ' );
1780  aLine.append( m_aStructParentTree[n] );
1781  aLine.append( "\n" );
1782  }
1783  aLine.append( "]>>\nendobj\n\n" );
1784  CHECK_RETURN( updateObject( nObject ) );
1785  CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
1786  }
1787  return nObject;
1788 }
1789 
1791 {
1792  static std::map< PDFWriter::StructAttribute, const char* > aAttributeStrings;
1793  // fill maps once
1794  if( aAttributeStrings.empty() )
1795  {
1796  aAttributeStrings[ PDFWriter::Placement ] = "Placement";
1797  aAttributeStrings[ PDFWriter::WritingMode ] = "WritingMode";
1798  aAttributeStrings[ PDFWriter::SpaceBefore ] = "SpaceBefore";
1799  aAttributeStrings[ PDFWriter::SpaceAfter ] = "SpaceAfter";
1800  aAttributeStrings[ PDFWriter::StartIndent ] = "StartIndent";
1801  aAttributeStrings[ PDFWriter::EndIndent ] = "EndIndent";
1802  aAttributeStrings[ PDFWriter::TextIndent ] = "TextIndent";
1803  aAttributeStrings[ PDFWriter::TextAlign ] = "TextAlign";
1804  aAttributeStrings[ PDFWriter::Width ] = "Width";
1805  aAttributeStrings[ PDFWriter::Height ] = "Height";
1806  aAttributeStrings[ PDFWriter::BlockAlign ] = "BlockAlign";
1807  aAttributeStrings[ PDFWriter::InlineAlign ] = "InlineAlign";
1808  aAttributeStrings[ PDFWriter::LineHeight ] = "LineHeight";
1809  aAttributeStrings[ PDFWriter::BaselineShift ] = "BaselineShift";
1810  aAttributeStrings[ PDFWriter::TextDecorationType ] = "TextDecorationType";
1811  aAttributeStrings[ PDFWriter::ListNumbering ] = "ListNumbering";
1812  aAttributeStrings[ PDFWriter::RowSpan ] = "RowSpan";
1813  aAttributeStrings[ PDFWriter::ColSpan ] = "ColSpan";
1814  aAttributeStrings[ PDFWriter::LinkAnnotation ] = "LinkAnnotation";
1815  }
1816 
1817  std::map< PDFWriter::StructAttribute, const char* >::const_iterator it =
1818  aAttributeStrings.find( eAttr );
1819 
1820  if( it == aAttributeStrings.end() )
1821  SAL_INFO("vcl.pdfwriter", "invalid PDFWriter::StructAttribute " << eAttr);
1822 
1823  return it != aAttributeStrings.end() ? it->second : "";
1824 }
1825 
1827 {
1828  static std::map< PDFWriter::StructAttributeValue, const char* > aValueStrings;
1829 
1830  if( aValueStrings.empty() )
1831  {
1832  aValueStrings[ PDFWriter::NONE ] = "None";
1833  aValueStrings[ PDFWriter::Block ] = "Block";
1834  aValueStrings[ PDFWriter::Inline ] = "Inline";
1835  aValueStrings[ PDFWriter::Before ] = "Before";
1836  aValueStrings[ PDFWriter::After ] = "After";
1837  aValueStrings[ PDFWriter::Start ] = "Start";
1838  aValueStrings[ PDFWriter::End ] = "End";
1839  aValueStrings[ PDFWriter::LrTb ] = "LrTb";
1840  aValueStrings[ PDFWriter::RlTb ] = "RlTb";
1841  aValueStrings[ PDFWriter::TbRl ] = "TbRl";
1842  aValueStrings[ PDFWriter::Center ] = "Center";
1843  aValueStrings[ PDFWriter::Justify ] = "Justify";
1844  aValueStrings[ PDFWriter::Auto ] = "Auto";
1845  aValueStrings[ PDFWriter::Middle ] = "Middle";
1846  aValueStrings[ PDFWriter::Normal ] = "Normal";
1847  aValueStrings[ PDFWriter::Underline ] = "Underline";
1848  aValueStrings[ PDFWriter::Overline ] = "Overline";
1849  aValueStrings[ PDFWriter::LineThrough ] = "LineThrough";
1850  aValueStrings[ PDFWriter::Disc ] = "Disc";
1851  aValueStrings[ PDFWriter::Circle ] = "Circle";
1852  aValueStrings[ PDFWriter::Square ] = "Square";
1853  aValueStrings[ PDFWriter::Decimal ] = "Decimal";
1854  aValueStrings[ PDFWriter::UpperRoman ] = "UpperRoman";
1855  aValueStrings[ PDFWriter::LowerRoman ] = "LowerRoman";
1856  aValueStrings[ PDFWriter::UpperAlpha ] = "UpperAlpha";
1857  aValueStrings[ PDFWriter::LowerAlpha ] = "LowerAlpha";
1858  }
1859 
1860  std::map< PDFWriter::StructAttributeValue, const char* >::const_iterator it =
1861  aValueStrings.find( eVal );
1862 
1863  if( it == aValueStrings.end() )
1864  SAL_INFO("vcl.pdfwriter", "invalid PDFWriter::StructAttributeValue " << eVal);
1865 
1866  return it != aValueStrings.end() ? it->second : "";
1867 }
1868 
1869 static void appendStructureAttributeLine( PDFWriter::StructAttribute i_eAttr, const PDFStructureAttribute& i_rVal, OStringBuffer& o_rLine, bool i_bIsFixedInt )
1870 {
1871  o_rLine.append( "/" );
1872  o_rLine.append( PDFWriterImpl::getAttributeTag( i_eAttr ) );
1873 
1874  if( i_rVal.eValue != PDFWriter::Invalid )
1875  {
1876  o_rLine.append( "/" );
1877  o_rLine.append( PDFWriterImpl::getAttributeValueTag( i_rVal.eValue ) );
1878  }
1879  else
1880  {
1881  // numerical value
1882  o_rLine.append( " " );
1883  if( i_bIsFixedInt )
1884  appendFixedInt( i_rVal.nValue, o_rLine );
1885  else
1886  o_rLine.append( i_rVal.nValue );
1887  }
1888  o_rLine.append( "\n" );
1889 }
1890 
1892 {
1893  // create layout, list and table attribute sets
1894  OStringBuffer aLayout(256), aList(64), aTable(64);
1895  for (auto const& attribute : i_rEle.m_aAttributes)
1896  {
1897  if( attribute.first == PDFWriter::ListNumbering )
1898  appendStructureAttributeLine( attribute.first, attribute.second, aList, true );
1899  else if( attribute.first == PDFWriter::RowSpan ||
1900  attribute.first == PDFWriter::ColSpan )
1901  appendStructureAttributeLine( attribute.first, attribute.second, aTable, false );
1902  else if( attribute.first == PDFWriter::LinkAnnotation )
1903  {
1904  sal_Int32 nLink = attribute.second.nValue;
1905  std::map< sal_Int32, sal_Int32 >::const_iterator link_it =
1906  m_aLinkPropertyMap.find( nLink );
1907  if( link_it != m_aLinkPropertyMap.end() )
1908  nLink = link_it->second;
1909  if( nLink >= 0 && nLink < static_cast<sal_Int32>(m_aLinks.size()) )
1910  {
1911  // update struct parent of link
1912  OString aStructParentEntry =
1913  OString::number( i_rEle.m_nObject ) +
1914  " 0 R";
1915  m_aStructParentTree.push_back( aStructParentEntry );
1916  m_aLinks[ nLink ].m_nStructParent = m_aStructParentTree.size()-1;
1917 
1918  sal_Int32 nRefObject = createObject();
1919  if (updateObject(nRefObject))
1920  {
1921  OString aRef =
1922  OString::number( nRefObject ) +
1923  " 0 obj\n"
1924  "<</Type/OBJR/Obj " +
1925  OString::number( m_aLinks[ nLink ].m_nObject ) +
1926  " 0 R>>\n"
1927  "endobj\n\n";
1928  writeBuffer( aRef.getStr(), aRef.getLength() );
1929  }
1930 
1931  i_rEle.m_aKids.emplace_back( nRefObject );
1932  }
1933  else
1934  {
1935  OSL_FAIL( "unresolved link id for Link structure" );
1936  SAL_INFO("vcl.pdfwriter", "unresolved link id " << nLink << " for Link structure");
1938  {
1939  OString aLine = "unresolved link id " +
1940  OString::number( nLink ) +
1941  " for Link structure";
1942  emitComment( aLine.getStr() );
1943  }
1944  }
1945  }
1946  else
1947  appendStructureAttributeLine( attribute.first, attribute.second, aLayout, true );
1948  }
1949  if( ! i_rEle.m_aBBox.IsEmpty() )
1950  {
1951  aLayout.append( "/BBox[" );
1952  appendFixedInt( i_rEle.m_aBBox.Left(), aLayout );
1953  aLayout.append( " " );
1954  appendFixedInt( i_rEle.m_aBBox.Top(), aLayout );
1955  aLayout.append( " " );
1956  appendFixedInt( i_rEle.m_aBBox.Right(), aLayout );
1957  aLayout.append( " " );
1958  appendFixedInt( i_rEle.m_aBBox.Bottom(), aLayout );
1959  aLayout.append( "]\n" );
1960  }
1961 
1962  std::vector< sal_Int32 > aAttribObjects;
1963  if( !aLayout.isEmpty() )
1964  {
1965  aAttribObjects.push_back( createObject() );
1966  if (updateObject( aAttribObjects.back() ))
1967  {
1968  OStringBuffer aObj( 64 );
1969  aObj.append( aAttribObjects.back() );
1970  aObj.append( " 0 obj\n"
1971  "<</O/Layout\n" );
1972  aLayout.append( ">>\nendobj\n\n" );
1973  writeBuffer( aObj.getStr(), aObj.getLength() );
1974  writeBuffer( aLayout.getStr(), aLayout.getLength() );
1975  }
1976  }
1977  if( !aList.isEmpty() )
1978  {
1979  aAttribObjects.push_back( createObject() );
1980  if (updateObject( aAttribObjects.back() ))
1981  {
1982  OStringBuffer aObj( 64 );
1983  aObj.append( aAttribObjects.back() );
1984  aObj.append( " 0 obj\n"
1985  "<</O/List\n" );
1986  aList.append( ">>\nendobj\n\n" );
1987  writeBuffer( aObj.getStr(), aObj.getLength() );
1988  writeBuffer( aList.getStr(), aList.getLength() );
1989  }
1990  }
1991  if( !aTable.isEmpty() )
1992  {
1993  aAttribObjects.push_back( createObject() );
1994  if (updateObject( aAttribObjects.back() ))
1995  {
1996  OStringBuffer aObj( 64 );
1997  aObj.append( aAttribObjects.back() );
1998  aObj.append( " 0 obj\n"
1999  "<</O/Table\n" );
2000  aTable.append( ">>\nendobj\n\n" );
2001  writeBuffer( aObj.getStr(), aObj.getLength() );
2002  writeBuffer( aTable.getStr(), aTable.getLength() );
2003  }
2004  }
2005 
2006  OStringBuffer aRet( 64 );
2007  if( aAttribObjects.size() > 1 )
2008  aRet.append( " [" );
2009  for (auto const& attrib : aAttribObjects)
2010  {
2011  aRet.append( " " );
2012  aRet.append( attrib );
2013  aRet.append( " 0 R" );
2014  }
2015  if( aAttribObjects.size() > 1 )
2016  aRet.append( " ]" );
2017  return aRet.makeStringAndClear();
2018 }
2019 
2021 {
2022  if(
2023  // do not emit NonStruct and its children
2025  rEle.m_nOwnElement != rEle.m_nParentElement // but of course emit the struct tree root
2026  )
2027  return 0;
2028 
2029  for (auto const& child : rEle.m_aChildren)
2030  {
2031  if( child > 0 && child < sal_Int32(m_aStructure.size()) )
2032  {
2033  PDFStructureElement& rChild = m_aStructure[ child ];
2034  if( rChild.m_eType != PDFWriter::NonStructElement )
2035  {
2036  if( rChild.m_nParentElement == rEle.m_nOwnElement )
2037  emitStructure( rChild );
2038  else
2039  {
2040  OSL_FAIL( "PDFWriterImpl::emitStructure: invalid child structure element" );
2041  SAL_INFO("vcl.pdfwriter", "PDFWriterImpl::emitStructure: invalid child structure element with id " << child);
2042  }
2043  }
2044  }
2045  else
2046  {
2047  OSL_FAIL( "PDFWriterImpl::emitStructure: invalid child structure id" );
2048  SAL_INFO("vcl.pdfwriter", "PDFWriterImpl::emitStructure: invalid child structure id " << child);
2049  }
2050  }
2051 
2052  OStringBuffer aLine( 512 );
2053  aLine.append( rEle.m_nObject );
2054  aLine.append( " 0 obj\n"
2055  "<</Type" );
2056  sal_Int32 nParentTree = -1;
2057  if( rEle.m_nOwnElement == rEle.m_nParentElement )
2058  {
2059  nParentTree = createObject();
2060  CHECK_RETURN( nParentTree );
2061  aLine.append( "/StructTreeRoot\n" );
2062  aLine.append( "/ParentTree " );
2063  aLine.append( nParentTree );
2064  aLine.append( " 0 R\n" );
2065  if( ! m_aRoleMap.empty() )
2066  {
2067  aLine.append( "/RoleMap<<" );
2068  for (auto const& role : m_aRoleMap)
2069  {
2070  aLine.append( '/' );
2071  aLine.append(role.first);
2072  aLine.append( '/' );
2073  aLine.append( role.second );
2074  aLine.append( '\n' );
2075  }
2076  aLine.append( ">>\n" );
2077  }
2078  }
2079  else
2080  {
2081  aLine.append( "/StructElem\n"
2082  "/S/" );
2083  if( !rEle.m_aAlias.isEmpty() )
2084  aLine.append( rEle.m_aAlias );
2085  else
2086  aLine.append( getStructureTag( rEle.m_eType ) );
2087  aLine.append( "\n"
2088  "/P " );
2089  aLine.append( m_aStructure[ rEle.m_nParentElement ].m_nObject );
2090  aLine.append( " 0 R\n"
2091  "/Pg " );
2092  aLine.append( rEle.m_nFirstPageObject );
2093  aLine.append( " 0 R\n" );
2094  if( !rEle.m_aActualText.isEmpty() )
2095  {
2096  aLine.append( "/ActualText" );
2098  aLine.append( "\n" );
2099  }
2100  if( !rEle.m_aAltText.isEmpty() )
2101  {
2102  aLine.append( "/Alt" );
2104  aLine.append( "\n" );
2105  }
2106  }
2107  if( (! rEle.m_aBBox.IsEmpty()) || (! rEle.m_aAttributes.empty()) )
2108  {
2109  OString aAttribs = emitStructureAttributes( rEle );
2110  if( !aAttribs.isEmpty() )
2111  {
2112  aLine.append( "/A" );
2113  aLine.append( aAttribs );
2114  aLine.append( "\n" );
2115  }
2116  }
2117  if( !rEle.m_aLocale.Language.isEmpty() )
2118  {
2119  /* PDF allows only RFC 3066, which is only partly BCP 47 and does not
2120  * include script tags and others.
2121  * http://pdf.editme.com/pdfua-naturalLanguageSpecification
2122  * http://partners.adobe.com/public/developer/en/pdf/PDFReference16.pdf#page=886
2123  * https://www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf#M13.9.19332.1Heading.97.Natural.Language.Specification
2124  * */
2125  LanguageTag aLanguageTag( rEle.m_aLocale);
2126  OUString aLanguage, aScript, aCountry;
2127  aLanguageTag.getIsoLanguageScriptCountry( aLanguage, aScript, aCountry);
2128  if (!aLanguage.isEmpty())
2129  {
2130  OUStringBuffer aLocBuf( 16 );
2131  aLocBuf.append( aLanguage );
2132  if( !aCountry.isEmpty() )
2133  {
2134  aLocBuf.append( '-' );
2135  aLocBuf.append( aCountry );
2136  }
2137  aLine.append( "/Lang" );
2138  appendLiteralStringEncrypt( aLocBuf.makeStringAndClear(), rEle.m_nObject, aLine );
2139  aLine.append( "\n" );
2140  }
2141  }
2142  if( ! rEle.m_aKids.empty() )
2143  {
2144  unsigned int i = 0;
2145  aLine.append( "/K[" );
2146  for (auto const& kid : rEle.m_aKids)
2147  {
2148  if( kid.nMCID == -1 )
2149  {
2150  aLine.append( kid.nObject );
2151  aLine.append( " 0 R" );
2152  aLine.append( ( (i & 15) == 15 ) ? "\n" : " " );
2153  }
2154  else
2155  {
2156  if( kid.nObject == rEle.m_nFirstPageObject )
2157  {
2158  aLine.append( kid.nMCID );
2159  aLine.append( " " );
2160  }
2161  else
2162  {
2163  aLine.append( "<</Type/MCR/Pg " );
2164  aLine.append( kid.nObject );
2165  aLine.append( " 0 R /MCID " );
2166  aLine.append( kid.nMCID );
2167  aLine.append( ">>\n" );
2168  }
2169  }
2170  ++i;
2171  }
2172  aLine.append( "]\n" );
2173  }
2174  aLine.append( ">>\nendobj\n\n" );
2175 
2176  CHECK_RETURN( updateObject( rEle.m_nObject ) );
2177  CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
2178 
2179  CHECK_RETURN( emitStructParentTree( nParentTree ) );
2180 
2181  return rEle.m_nObject;
2182 }
2183 
2185 {
2186  for (auto const& gradient : m_aGradients)
2187  {
2188  if ( !writeGradientFunction( gradient ) ) return false;
2189  }
2190  return true;
2191 }
2192 
2194 {
2195  OStringBuffer aTilingObj( 1024 );
2196 
2197  for (auto & tiling : m_aTilings)
2198  {
2199  SAL_WARN_IF( !tiling.m_pTilingStream, "vcl.pdfwriter", "tiling without stream" );
2200  if( ! tiling.m_pTilingStream )
2201  continue;
2202 
2203  aTilingObj.setLength( 0 );
2204 
2206  {
2207  emitComment( "PDFWriterImpl::emitTilings" );
2208  }
2209 
2210  sal_Int32 nX = static_cast<sal_Int32>(tiling.m_aRectangle.Left());
2211  sal_Int32 nY = static_cast<sal_Int32>(tiling.m_aRectangle.Top());
2212  sal_Int32 nW = static_cast<sal_Int32>(tiling.m_aRectangle.GetWidth());
2213  sal_Int32 nH = static_cast<sal_Int32>(tiling.m_aRectangle.GetHeight());
2214  if( tiling.m_aCellSize.Width() == 0 )
2215  tiling.m_aCellSize.setWidth( nW );
2216  if( tiling.m_aCellSize.Height() == 0 )
2217  tiling.m_aCellSize.setHeight( nH );
2218 
2219  bool bDeflate = compressStream( tiling.m_pTilingStream.get() );
2220  sal_uInt64 const nTilingStreamSize = tiling.m_pTilingStream->TellEnd();
2221  tiling.m_pTilingStream->Seek( STREAM_SEEK_TO_BEGIN );
2222 
2223  // write pattern object
2224  aTilingObj.append( tiling.m_nObject );
2225  aTilingObj.append( " 0 obj\n" );
2226  aTilingObj.append( "<</Type/Pattern/PatternType 1\n"
2227  "/PaintType 1\n"
2228  "/TilingType 2\n"
2229  "/BBox[" );
2230  appendFixedInt( nX, aTilingObj );
2231  aTilingObj.append( ' ' );
2232  appendFixedInt( nY, aTilingObj );
2233  aTilingObj.append( ' ' );
2234  appendFixedInt( nX+nW, aTilingObj );
2235  aTilingObj.append( ' ' );
2236  appendFixedInt( nY+nH, aTilingObj );
2237  aTilingObj.append( "]\n"
2238  "/XStep " );
2239  appendFixedInt( tiling.m_aCellSize.Width(), aTilingObj );
2240  aTilingObj.append( "\n"
2241  "/YStep " );
2242  appendFixedInt( tiling.m_aCellSize.Height(), aTilingObj );
2243  aTilingObj.append( "\n" );
2244  if( tiling.m_aTransform.matrix[0] != 1.0 ||
2245  tiling.m_aTransform.matrix[1] != 0.0 ||
2246  tiling.m_aTransform.matrix[3] != 0.0 ||
2247  tiling.m_aTransform.matrix[4] != 1.0 ||
2248  tiling.m_aTransform.matrix[2] != 0.0 ||
2249  tiling.m_aTransform.matrix[5] != 0.0 )
2250  {
2251  aTilingObj.append( "/Matrix [" );
2252  // TODO: scaling, mirroring on y, etc
2253  appendDouble( tiling.m_aTransform.matrix[0], aTilingObj );
2254  aTilingObj.append( ' ' );
2255  appendDouble( tiling.m_aTransform.matrix[1], aTilingObj );
2256  aTilingObj.append( ' ' );
2257  appendDouble( tiling.m_aTransform.matrix[3], aTilingObj );
2258  aTilingObj.append( ' ' );
2259  appendDouble( tiling.m_aTransform.matrix[4], aTilingObj );
2260  aTilingObj.append( ' ' );
2261  appendDouble( tiling.m_aTransform.matrix[2], aTilingObj );
2262  aTilingObj.append( ' ' );
2263  appendDouble( tiling.m_aTransform.matrix[5], aTilingObj );
2264  aTilingObj.append( "]\n" );
2265  }
2266  aTilingObj.append( "/Resources" );
2267  tiling.m_aResources.append( aTilingObj, getFontDictObject() );
2268  if( bDeflate )
2269  aTilingObj.append( "/Filter/FlateDecode" );
2270  aTilingObj.append( "/Length " );
2271  aTilingObj.append( static_cast<sal_Int32>(nTilingStreamSize) );
2272  aTilingObj.append( ">>\nstream\n" );
2273  if ( !updateObject( tiling.m_nObject ) ) return false;
2274  if ( !writeBuffer( aTilingObj.getStr(), aTilingObj.getLength() ) ) return false;
2275  checkAndEnableStreamEncryption( tiling.m_nObject );
2276  bool written = writeBuffer( tiling.m_pTilingStream->GetData(), nTilingStreamSize );
2277  tiling.m_pTilingStream.reset();
2278  if( !written )
2279  return false;
2281  aTilingObj.setLength( 0 );
2282  aTilingObj.append( "\nendstream\nendobj\n\n" );
2283  if ( !writeBuffer( aTilingObj.getStr(), aTilingObj.getLength() ) ) return false;
2284  }
2285  return true;
2286 }
2287 
2288 sal_Int32 PDFWriterImpl::emitBuildinFont(const pdf::BuildinFontFace* pFD, sal_Int32 nFontObject)
2289 {
2290  if( !pFD )
2291  return 0;
2292  const pdf::BuildinFont& rBuildinFont = pFD->GetBuildinFont();
2293 
2294  OStringBuffer aLine( 1024 );
2295 
2296  if( nFontObject <= 0 )
2297  nFontObject = createObject();
2298  CHECK_RETURN( updateObject( nFontObject ) );
2299  aLine.append( nFontObject );
2300  aLine.append( " 0 obj\n"
2301  "<</Type/Font/Subtype/Type1/BaseFont/" );
2302  appendName( rBuildinFont.m_pPSName, aLine );
2303  aLine.append( "\n" );
2304  if( rBuildinFont.m_eCharSet == RTL_TEXTENCODING_MS_1252 )
2305  aLine.append( "/Encoding/WinAnsiEncoding\n" );
2306  aLine.append( ">>\nendobj\n\n" );
2307  CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
2308  return nFontObject;
2309 }
2310 
2311 std::map< sal_Int32, sal_Int32 > PDFWriterImpl::emitSystemFont( const PhysicalFontFace* pFont, EmbedFont const & rEmbed )
2312 {
2313  std::map< sal_Int32, sal_Int32 > aRet;
2314 
2315  sal_Int32 nFontDescriptor = 0;
2316  OString aSubType( "/Type1" );
2317  FontSubsetInfo aInfo;
2318  // fill in dummy values
2319  aInfo.m_nAscent = 1000;
2320  aInfo.m_nDescent = 200;
2321  aInfo.m_nCapHeight = 1000;
2322  aInfo.m_aFontBBox = tools::Rectangle( Point( -200, -200 ), Size( 1700, 1700 ) );
2323  aInfo.m_aPSName = pFont->GetFamilyName();
2324  sal_Int32 pWidths[256] = {};
2325 
2326  SalGraphics *pGraphics = GetGraphics();
2327 
2328  assert(pGraphics);
2329 
2330  aSubType = OString( "/TrueType" );
2331  std::vector< sal_Int32 > aGlyphWidths;
2332  Ucs2UIntMap aUnicodeMap;
2333  pGraphics->GetGlyphWidths( pFont, false, aGlyphWidths, aUnicodeMap );
2334 
2335  OUString aTmpName;
2336  osl_createTempFile( nullptr, nullptr, &aTmpName.pData );
2337  sal_GlyphId aGlyphIds[ 256 ] = {};
2338  sal_uInt8 pEncoding[ 256 ] = {};
2339  sal_Int32 pDuWidths[ 256 ] = {};
2340 
2341  for( sal_Ucs c = 32; c < 256; c++ )
2342  {
2343  pEncoding[c] = c;
2344  aGlyphIds[c] = 0;
2345  if( aUnicodeMap.find( c ) != aUnicodeMap.end() )
2346  pWidths[ c ] = aGlyphWidths[ aUnicodeMap[ c ] ];
2347  }
2348  //TODO: surely this is utterly broken because aGlyphIds is just all zeros, if we
2349  //had the right glyphids here then I imagine we could replace pDuWidths with
2350  //pWidths and remove pWidths assignment above. i.e. start with the glyph ids
2351  //and map those to unicode rather than try and reverse map them ?
2352  pGraphics->CreateFontSubset( aTmpName, pFont, aGlyphIds, pEncoding, pDuWidths, 256, aInfo );
2353  osl_removeFile( aTmpName.pData );
2354 
2355  // write font descriptor
2356  nFontDescriptor = emitFontDescriptor( pFont, aInfo, 0, 0 );
2357  if( nFontDescriptor )
2358  {
2359  // write font object
2360  sal_Int32 nObject = createObject();
2361  if( updateObject( nObject ) )
2362  {
2363  OStringBuffer aLine( 1024 );
2364  aLine.append( nObject );
2365  aLine.append( " 0 obj\n"
2366  "<</Type/Font/Subtype" );
2367  aLine.append( aSubType );
2368  aLine.append( "/BaseFont/" );
2369  appendName( aInfo.m_aPSName, aLine );
2370  aLine.append( "\n" );
2371  if( !pFont->IsSymbolFont() )
2372  aLine.append( "/Encoding/WinAnsiEncoding\n" );
2373  aLine.append( "/FirstChar 32 /LastChar 255\n"
2374  "/Widths[" );
2375  for( int i = 32; i < 256; i++ )
2376  {
2377  aLine.append( pWidths[i] );
2378  aLine.append( ((i&15) == 15) ? "\n" : " " );
2379  }
2380  aLine.append( "]\n"
2381  "/FontDescriptor " );
2382  aLine.append( nFontDescriptor );
2383  aLine.append( " 0 R>>\n"
2384  "endobj\n\n" );
2385  writeBuffer( aLine.getStr(), aLine.getLength() );
2386 
2387  aRet[ rEmbed.m_nNormalFontID ] = nObject;
2388  }
2389  }
2390 
2391  return aRet;
2392 }
2393 
2394 typedef int ThreeInts[3];
2395 static bool getPfbSegmentLengths( const unsigned char* pFontBytes, int nByteLen,
2396  ThreeInts& rSegmentLengths )
2397 {
2398  if( !pFontBytes || (nByteLen < 0) )
2399  return false;
2400  const unsigned char* pPtr = pFontBytes;
2401  const unsigned char* pEnd = pFontBytes + nByteLen;
2402 
2403  for(int & rSegmentLength : rSegmentLengths) {
2404  // read segment1 header
2405  if( pPtr+6 >= pEnd )
2406  return false;
2407  if( (pPtr[0] != 0x80) || (pPtr[1] >= 0x03) )
2408  return false;
2409  const int nLen = (pPtr[5]<<24) + (pPtr[4]<<16) + (pPtr[3]<<8) + pPtr[2];
2410  if( nLen <= 0)
2411  return false;
2412  rSegmentLength = nLen;
2413  pPtr += nLen + 6;
2414  }
2415 
2416  // read segment-end header
2417  if( pPtr+2 >= pEnd )
2418  return false;
2419  if( (pPtr[0] != 0x80) || (pPtr[1] != 0x03) )
2420  return false;
2421 
2422  return true;
2423 }
2424 
2425 static void appendSubsetName( int nSubsetID, const OUString& rPSName, OStringBuffer& rBuffer )
2426 {
2427  if( nSubsetID )
2428  {
2429  for( int i = 0; i < 6; i++ )
2430  {
2431  int nOffset = nSubsetID % 26;
2432  nSubsetID /= 26;
2433  rBuffer.append( static_cast<char>('A'+nOffset) );
2434  }
2435  rBuffer.append( '+' );
2436  }
2437  appendName( rPSName, rBuffer );
2438 }
2439 
2440 sal_Int32 PDFWriterImpl::createToUnicodeCMap( sal_uInt8 const * pEncoding,
2441  const sal_Ucs* pCodeUnits,
2442  const sal_Int32* pCodeUnitsPerGlyph,
2443  const sal_Int32* pEncToUnicodeIndex,
2444  int nGlyphs )
2445 {
2446  int nMapped = 0;
2447  for (int n = 0; n < nGlyphs; ++n)
2448  if( pCodeUnits[pEncToUnicodeIndex[n]] && pCodeUnitsPerGlyph[n] )
2449  nMapped++;
2450 
2451  if( nMapped == 0 )
2452  return 0;
2453 
2454  sal_Int32 nStream = createObject();
2455  CHECK_RETURN( updateObject( nStream ) );
2456 
2457  OStringBuffer aContents( 1024 );
2458  aContents.append(
2459  "/CIDInit/ProcSet findresource begin\n"
2460  "12 dict begin\n"
2461  "begincmap\n"
2462  "/CIDSystemInfo<<\n"
2463  "/Registry (Adobe)\n"
2464  "/Ordering (UCS)\n"
2465  "/Supplement 0\n"
2466  ">> def\n"
2467  "/CMapName/Adobe-Identity-UCS def\n"
2468  "/CMapType 2 def\n"
2469  "1 begincodespacerange\n"
2470  "<00> <FF>\n"
2471  "endcodespacerange\n"
2472  );
2473  int nCount = 0;
2474  for (int n = 0; n < nGlyphs; ++n)
2475  {
2476  if( pCodeUnits[pEncToUnicodeIndex[n]] && pCodeUnitsPerGlyph[n] )
2477  {
2478  if( (nCount % 100) == 0 )
2479  {
2480  if( nCount )
2481  aContents.append( "endbfchar\n" );
2482  aContents.append( static_cast<sal_Int32>(std::min(nMapped-nCount, 100)) );
2483  aContents.append( " beginbfchar\n" );
2484  }
2485  aContents.append( '<' );
2486  appendHex( static_cast<sal_Int8>(pEncoding[n]), aContents );
2487  aContents.append( "> <" );
2488  // TODO: handle code points>U+FFFF
2489  sal_Int32 nIndex = pEncToUnicodeIndex[n];
2490  for( sal_Int32 j = 0; j < pCodeUnitsPerGlyph[n]; j++ )
2491  {
2492  appendHex( static_cast<sal_Int8>(pCodeUnits[nIndex + j] / 256), aContents );
2493  appendHex( static_cast<sal_Int8>(pCodeUnits[nIndex + j] & 255), aContents );
2494  }
2495  aContents.append( ">\n" );
2496  nCount++;
2497  }
2498  }
2499  aContents.append( "endbfchar\n"
2500  "endcmap\n"
2501  "CMapName currentdict /CMap defineresource pop\n"
2502  "end\n"
2503  "end\n" );
2504  SvMemoryStream aStream;
2506  {
2507  ZCodec aCodec( 0x4000, 0x4000 );
2508  aCodec.BeginCompression();
2509  aCodec.Write( aStream, reinterpret_cast<const sal_uInt8*>(aContents.getStr()), aContents.getLength() );
2510  aCodec.EndCompression();
2511  }
2512 
2514  {
2515  emitComment( "PDFWriterImpl::createToUnicodeCMap" );
2516  }
2517  OStringBuffer aLine( 40 );
2518 
2519  aLine.append( nStream );
2520  aLine.append( " 0 obj\n<</Length " );
2521  sal_Int32 nLen = 0;
2523  {
2524  nLen = static_cast<sal_Int32>(aStream.Tell());
2525  aStream.Seek( 0 );
2526  aLine.append( nLen );
2527  aLine.append( "/Filter/FlateDecode" );
2528  }
2529  else
2530  aLine.append( aContents.getLength() );
2531  aLine.append( ">>\nstream\n" );
2532  CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
2533  checkAndEnableStreamEncryption( nStream );
2535  {
2536  CHECK_RETURN( writeBuffer( aStream.GetData(), nLen ) );
2537  }
2538  else
2539  {
2540  CHECK_RETURN( writeBuffer( aContents.getStr(), aContents.getLength() ) );
2541  }
2543  aLine.setLength( 0 );
2544  aLine.append( "\nendstream\n"
2545  "endobj\n\n" );
2546  CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
2547  return nStream;
2548 }
2549 
2550 sal_Int32 PDFWriterImpl::emitFontDescriptor( const PhysicalFontFace* pFont, FontSubsetInfo const & rInfo, sal_Int32 nSubsetID, sal_Int32 nFontStream )
2551 {
2552  OStringBuffer aLine( 1024 );
2553  // get font flags, see PDF reference 1.4 p. 358
2554  // possibly characters outside Adobe standard encoding
2555  // so set Symbolic flag
2556  sal_Int32 nFontFlags = (1<<2);
2557  if( pFont->GetItalic() == ITALIC_NORMAL || pFont->GetItalic() == ITALIC_OBLIQUE )
2558  nFontFlags |= (1 << 6);
2559  if( pFont->GetPitch() == PITCH_FIXED )
2560  nFontFlags |= 1;
2561  if( pFont->GetFamilyType() == FAMILY_SCRIPT )
2562  nFontFlags |= (1 << 3);
2563  else if( pFont->GetFamilyType() == FAMILY_ROMAN )
2564  nFontFlags |= (1 << 1);
2565 
2566  sal_Int32 nFontDescriptor = createObject();
2567  CHECK_RETURN( updateObject( nFontDescriptor ) );
2568  aLine.setLength( 0 );
2569  aLine.append( nFontDescriptor );
2570  aLine.append( " 0 obj\n"
2571  "<</Type/FontDescriptor/FontName/" );
2572  appendSubsetName( nSubsetID, rInfo.m_aPSName, aLine );
2573  aLine.append( "\n"
2574  "/Flags " );
2575  aLine.append( nFontFlags );
2576  aLine.append( "\n"
2577  "/FontBBox[" );
2578  // note: Top and Bottom are reversed in VCL and PDF rectangles
2579  aLine.append( static_cast<sal_Int32>(rInfo.m_aFontBBox.TopLeft().X()) );
2580  aLine.append( ' ' );
2581  aLine.append( static_cast<sal_Int32>(rInfo.m_aFontBBox.TopLeft().Y()) );
2582  aLine.append( ' ' );
2583  aLine.append( static_cast<sal_Int32>(rInfo.m_aFontBBox.BottomRight().X()) );
2584  aLine.append( ' ' );
2585  aLine.append( static_cast<sal_Int32>(rInfo.m_aFontBBox.BottomRight().Y()+1) );
2586  aLine.append( "]/ItalicAngle " );
2587  if( pFont->GetItalic() == ITALIC_OBLIQUE || pFont->GetItalic() == ITALIC_NORMAL )
2588  aLine.append( "-30" );
2589  else
2590  aLine.append( "0" );
2591  aLine.append( "\n"
2592  "/Ascent " );
2593  aLine.append( static_cast<sal_Int32>(rInfo.m_nAscent) );
2594  aLine.append( "\n"
2595  "/Descent " );
2596  aLine.append( static_cast<sal_Int32>(-rInfo.m_nDescent) );
2597  aLine.append( "\n"
2598  "/CapHeight " );
2599  aLine.append( static_cast<sal_Int32>(rInfo.m_nCapHeight) );
2600  // According to PDF reference 1.4 StemV is required
2601  // seems a tad strange to me, but well ...
2602  aLine.append( "\n"
2603  "/StemV 80\n" );
2604  if( nFontStream )
2605  {
2606  aLine.append( "/FontFile" );
2607  switch( rInfo.m_nFontType )
2608  {
2609  case FontType::SFNT_TTF:
2610  aLine.append( '2' );
2611  break;
2612  case FontType::TYPE1_PFA:
2613  case FontType::TYPE1_PFB:
2614  case FontType::ANY_TYPE1:
2615  break;
2616  default:
2617  OSL_FAIL( "unknown fonttype in PDF font descriptor" );
2618  return 0;
2619  }
2620  aLine.append( ' ' );
2621  aLine.append( nFontStream );
2622  aLine.append( " 0 R\n" );
2623  }
2624  aLine.append( ">>\n"
2625  "endobj\n\n" );
2626  CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
2627 
2628  return nFontDescriptor;
2629 }
2630 
2631 void PDFWriterImpl::appendBuildinFontsToDict( OStringBuffer& rDict ) const
2632 {
2633  for (auto const& item : m_aBuildinFontToObjectMap)
2634  {
2635  rDict.append( pdf::BuildinFontFace::Get(item.first).getNameObject() );
2636  rDict.append( ' ' );
2637  rDict.append( item.second );
2638  rDict.append( " 0 R" );
2639  }
2640 }
2641 
2643 {
2644  SalGraphics *pGraphics = GetGraphics();
2645 
2646  if (!pGraphics)
2647  return false;
2648 
2649  OStringBuffer aLine( 1024 );
2650 
2651  std::map< sal_Int32, sal_Int32 > aFontIDToObject;
2652 
2653  OUString aTmpName;
2654  osl_createTempFile( nullptr, nullptr, &aTmpName.pData );
2655  for (const auto & subset : m_aSubsets)
2656  {
2657  for (auto & s_subset :subset.second.m_aSubsets)
2658  {
2659  sal_GlyphId aGlyphIds[ 256 ] = {};
2660  sal_Int32 pWidths[ 256 ];
2661  sal_uInt8 pEncoding[ 256 ] = {};
2662  sal_Int32 pEncToUnicodeIndex[ 256 ] = {};
2663  sal_Int32 pCodeUnitsPerGlyph[ 256 ] = {};
2664  std::vector<sal_Ucs> aCodeUnits;
2665  aCodeUnits.reserve( 256 );
2666  int nGlyphs = 1;
2667  // fill arrays and prepare encoding index map
2668  sal_Int32 nToUnicodeStream = 0;
2669 
2670  for (auto const& item : s_subset.m_aMapping)
2671  {
2672  sal_uInt8 nEnc = item.second.getGlyphId();
2673 
2674  SAL_WARN_IF( aGlyphIds[nEnc] != 0 || pEncoding[nEnc] != 0, "vcl.pdfwriter", "duplicate glyph" );
2675  SAL_WARN_IF( nEnc > s_subset.m_aMapping.size(), "vcl.pdfwriter", "invalid glyph encoding" );
2676 
2677  aGlyphIds[ nEnc ] = item.first;
2678  pEncoding[ nEnc ] = nEnc;
2679  pEncToUnicodeIndex[ nEnc ] = static_cast<sal_Int32>(aCodeUnits.size());
2680  pCodeUnitsPerGlyph[ nEnc ] = item.second.countCodes();
2681  for( sal_Int32 n = 0; n < pCodeUnitsPerGlyph[ nEnc ]; n++ )
2682  aCodeUnits.push_back( item.second.getCode( n ) );
2683  if( item.second.getCode(0) )
2684  nToUnicodeStream = 1;
2685  if( nGlyphs < 256 )
2686  nGlyphs++;
2687  else
2688  {
2689  OSL_FAIL( "too many glyphs for subset" );
2690  }
2691  }
2692  FontSubsetInfo aSubsetInfo;
2693  if( pGraphics->CreateFontSubset( aTmpName, subset.first, aGlyphIds, pEncoding, pWidths, nGlyphs, aSubsetInfo ) )
2694  {
2695  // create font stream
2696  osl::File aFontFile(aTmpName);
2697  if (osl::File::E_None != aFontFile.open(osl_File_OpenFlag_Read)) return false;
2698  // get file size
2699  sal_uInt64 nLength1;
2700  if ( osl::File::E_None != aFontFile.setPos(osl_Pos_End, 0) ) return false;
2701  if ( osl::File::E_None != aFontFile.getPos(nLength1) ) return false;
2702  if ( osl::File::E_None != aFontFile.setPos(osl_Pos_Absolut, 0) ) return false;
2703 
2705  {
2706  emitComment( "PDFWriterImpl::emitFonts" );
2707  }
2708  sal_Int32 nFontStream = createObject();
2709  sal_Int32 nStreamLengthObject = createObject();
2710  if ( !updateObject( nFontStream ) ) return false;
2711  aLine.setLength( 0 );
2712  aLine.append( nFontStream );
2713  aLine.append( " 0 obj\n"
2714  "<</Length " );
2715  aLine.append( nStreamLengthObject );
2717  aLine.append( " 0 R"
2718  "/Filter/FlateDecode"
2719  "/Length1 " );
2720  else
2721  aLine.append( " 0 R"
2722  "/Length1 " );
2723 
2724  sal_uInt64 nStartPos = 0;
2725  if( aSubsetInfo.m_nFontType == FontType::SFNT_TTF )
2726  {
2727  aLine.append( static_cast<sal_Int32>(nLength1) );
2728 
2729  aLine.append( ">>\n"
2730  "stream\n" );
2731  if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return false;
2732  if ( osl::File::E_None != m_aFile.getPos(nStartPos) ) return false;
2733 
2734  // copy font file
2735  beginCompression();
2736  checkAndEnableStreamEncryption( nFontStream );
2737  sal_Bool bEOF = false;
2738  do
2739  {
2740  char buf[8192];
2741  sal_uInt64 nRead;
2742  if ( osl::File::E_None != aFontFile.read(buf, sizeof(buf), nRead) ) return false;
2743  if ( !writeBuffer( buf, nRead ) ) return false;
2744  if ( osl::File::E_None != aFontFile.isEndOfFile(&bEOF) ) return false;
2745  } while( ! bEOF );
2746  }
2747  else if( aSubsetInfo.m_nFontType & FontType::CFF_FONT)
2748  {
2749  // TODO: implement
2750  OSL_FAIL( "PDFWriterImpl does not support CFF-font subsets yet!" );
2751  }
2752  else if( aSubsetInfo.m_nFontType & FontType::TYPE1_PFB) // TODO: also support PFA?
2753  {
2754  std::unique_ptr<unsigned char[]> xBuffer(new unsigned char[nLength1]);
2755 
2756  sal_uInt64 nBytesRead = 0;
2757  if ( osl::File::E_None != aFontFile.read(xBuffer.get(), nLength1, nBytesRead) ) return false;
2758  SAL_WARN_IF( nBytesRead!=nLength1, "vcl.pdfwriter", "PDF-FontSubset read incomplete!" );
2759  if ( osl::File::E_None != aFontFile.setPos(osl_Pos_Absolut, 0) ) return false;
2760  // get the PFB-segment lengths
2761  ThreeInts aSegmentLengths = {0,0,0};
2762  getPfbSegmentLengths(xBuffer.get(), static_cast<int>(nBytesRead), aSegmentLengths);
2763  // the lengths below are mandatory for PDF-exported Type1 fonts
2764  // because the PFB segment headers get stripped! WhyOhWhy.
2765  aLine.append( static_cast<sal_Int32>(aSegmentLengths[0]) );
2766  aLine.append( "/Length2 " );
2767  aLine.append( static_cast<sal_Int32>(aSegmentLengths[1]) );
2768  aLine.append( "/Length3 " );
2769  aLine.append( static_cast<sal_Int32>(aSegmentLengths[2]) );
2770 
2771  aLine.append( ">>\n"
2772  "stream\n" );
2773  if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return false;
2774  if ( osl::File::E_None != m_aFile.getPos(nStartPos) ) return false;
2775 
2776  // emit PFB-sections without section headers
2777  beginCompression();
2778  checkAndEnableStreamEncryption( nFontStream );
2779  if ( !writeBuffer( &xBuffer[6], aSegmentLengths[0] ) ) return false;
2780  if ( !writeBuffer( &xBuffer[12] + aSegmentLengths[0], aSegmentLengths[1] ) ) return false;
2781  if ( !writeBuffer( &xBuffer[18] + aSegmentLengths[0] + aSegmentLengths[1], aSegmentLengths[2] ) ) return false;
2782  }
2783  else
2784  {
2785  SAL_INFO("vcl.pdfwriter", "PDF: CreateFontSubset result in not yet supported format=" << static_cast<int>(aSubsetInfo.m_nFontType));
2786  aLine.append( "0 >>\nstream\n" );
2787  }
2788 
2789  endCompression();
2791  // close the file
2792  aFontFile.close();
2793 
2794  sal_uInt64 nEndPos = 0;
2795  if ( osl::File::E_None != m_aFile.getPos(nEndPos) ) return false;
2796  // end the stream
2797  aLine.setLength( 0 );
2798  aLine.append( "\nendstream\nendobj\n\n" );
2799  if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return false;
2800 
2801  // emit stream length object
2802  if ( !updateObject( nStreamLengthObject ) ) return false;
2803  aLine.setLength( 0 );
2804  aLine.append( nStreamLengthObject );
2805  aLine.append( " 0 obj\n" );
2806  aLine.append( static_cast<sal_Int64>(nEndPos-nStartPos) );
2807  aLine.append( "\nendobj\n\n" );
2808  if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return false;
2809 
2810  // write font descriptor
2811  sal_Int32 nFontDescriptor = emitFontDescriptor( subset.first, aSubsetInfo, s_subset.m_nFontID, nFontStream );
2812 
2813  if( nToUnicodeStream )
2814  nToUnicodeStream = createToUnicodeCMap( pEncoding, aCodeUnits.data(), pCodeUnitsPerGlyph, pEncToUnicodeIndex, nGlyphs );
2815 
2816  sal_Int32 nFontObject = createObject();
2817  if ( !updateObject( nFontObject ) ) return false;
2818  aLine.setLength( 0 );
2819  aLine.append( nFontObject );
2820 
2821  aLine.append( " 0 obj\n" );
2822  aLine.append( (aSubsetInfo.m_nFontType & FontType::ANY_TYPE1) ?
2823  "<</Type/Font/Subtype/Type1/BaseFont/" :
2824  "<</Type/Font/Subtype/TrueType/BaseFont/" );
2825  appendSubsetName( s_subset.m_nFontID, aSubsetInfo.m_aPSName, aLine );
2826  aLine.append( "\n"
2827  "/FirstChar 0\n"
2828  "/LastChar " );
2829  aLine.append( static_cast<sal_Int32>(nGlyphs-1) );
2830  aLine.append( "\n"
2831  "/Widths[" );
2832  for( int i = 0; i < nGlyphs; i++ )
2833  {
2834  aLine.append( pWidths[ i ] );
2835  aLine.append( ((i & 15) == 15) ? "\n" : " " );
2836  }
2837  aLine.append( "]\n"
2838  "/FontDescriptor " );
2839  aLine.append( nFontDescriptor );
2840  aLine.append( " 0 R\n" );
2841  if( nToUnicodeStream )
2842  {
2843  aLine.append( "/ToUnicode " );
2844  aLine.append( nToUnicodeStream );
2845  aLine.append( " 0 R\n" );
2846  }
2847  aLine.append( ">>\n"
2848  "endobj\n\n" );
2849  if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return false;
2850 
2851  aFontIDToObject[ s_subset.m_nFontID ] = nFontObject;
2852  }
2853  else
2854  {
2855  const PhysicalFontFace* pFont = subset.first;
2856  OStringBuffer aErrorComment( 256 );
2857  aErrorComment.append( "CreateFontSubset failed for font \"" );
2858  aErrorComment.append( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) );
2859  aErrorComment.append( '\"' );
2860  if( pFont->GetItalic() == ITALIC_NORMAL )
2861  aErrorComment.append( " italic" );
2862  else if( pFont->GetItalic() == ITALIC_OBLIQUE )
2863  aErrorComment.append( " oblique" );
2864  aErrorComment.append( " weight=" );
2865  aErrorComment.append( sal_Int32(pFont->GetWeight()) );
2866  emitComment( aErrorComment.getStr() );
2867  }
2868  }
2869  }
2870  osl_removeFile( aTmpName.pData );
2871 
2872  // emit system fonts
2873  for (auto const& systemFont : m_aSystemFonts)
2874  {
2875  std::map< sal_Int32, sal_Int32 > aObjects = emitSystemFont( systemFont.first, systemFont.second );
2876  for (auto const& item : aObjects)
2877  {
2878  if ( !item.second ) return false;
2879  aFontIDToObject[ item.first ] = item.second;
2880  }
2881  }
2882 
2883  OStringBuffer aFontDict( 1024 );
2884  aFontDict.append( getFontDictObject() );
2885  aFontDict.append( " 0 obj\n"
2886  "<<" );
2887  int ni = 0;
2888  for (auto const& itemMap : aFontIDToObject)
2889  {
2890  aFontDict.append( "/F" );
2891  aFontDict.append( itemMap.first );
2892  aFontDict.append( ' ' );
2893  aFontDict.append( itemMap.second );
2894  aFontDict.append( " 0 R" );
2895  if( ((++ni) & 7) == 0 )
2896  aFontDict.append( '\n' );
2897  }
2898  // emit builtin font for widget appearances / variable text
2899  for (auto & item : m_aBuildinFontToObjectMap)
2900  {
2902  item.second = emitBuildinFont( aData.get(), item.second );
2903  }
2904 
2905  appendBuildinFontsToDict(aFontDict);
2906  aFontDict.append( "\n>>\nendobj\n\n" );
2907 
2908  if ( !updateObject( getFontDictObject() ) ) return false;
2909  if ( !writeBuffer( aFontDict.getStr(), aFontDict.getLength() ) ) return false;
2910  return true;
2911 }
2912 
2914 {
2915  // emit shadings
2916  if( ! m_aGradients.empty() )
2918  // emit tilings
2919  if( ! m_aTilings.empty() )
2921 
2922  // emit font dict
2923  CHECK_RETURN( emitFonts() );
2924 
2925  // emit Resource dict
2926  OStringBuffer aLine( 512 );
2927  sal_Int32 nResourceDict = getResourceDictObj();
2928  CHECK_RETURN( updateObject( nResourceDict ) );
2929  aLine.setLength( 0 );
2930  aLine.append( nResourceDict );
2931  aLine.append( " 0 obj\n" );
2933  aLine.append( "endobj\n\n" );
2934  CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
2935  return nResourceDict;
2936 }
2937 
2938 sal_Int32 PDFWriterImpl::updateOutlineItemCount( std::vector< sal_Int32 >& rCounts,
2939  sal_Int32 nItemLevel,
2940  sal_Int32 nCurrentItemId )
2941 {
2942  /* The /Count number of an item is
2943  positive: the number of visible subitems
2944  negative: the negative number of subitems that will become visible if
2945  the item gets opened
2946  see PDF ref 1.4 p 478
2947  */
2948 
2949  sal_Int32 nCount = 0;
2950 
2951  if( m_aContext.OpenBookmarkLevels < 0 || // all levels are visible
2952  m_aContext.OpenBookmarkLevels >= nItemLevel // this level is visible
2953  )
2954  {
2955  PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ];
2956  sal_Int32 nChildren = rItem.m_aChildren.size();
2957  for( sal_Int32 i = 0; i < nChildren; i++ )
2958  nCount += updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] );
2959  rCounts[nCurrentItemId] = nCount;
2960  // return 1 (this item) + visible sub items
2961  if( nCount < 0 )
2962  nCount = 0;
2963  nCount++;
2964  }
2965  else
2966  {
2967  // this bookmark level is invisible
2968  PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ];
2969  sal_Int32 nChildren = rItem.m_aChildren.size();
2970  rCounts[ nCurrentItemId ] = -sal_Int32(rItem.m_aChildren.size());
2971  for( sal_Int32 i = 0; i < nChildren; i++ )
2972  updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] );
2973  nCount = -1;
2974  }
2975 
2976  return nCount;
2977 }
2978 
2980 {
2981  int i, nItems = m_aOutline.size();
2982 
2983  // do we have an outline at all ?
2984  if( nItems < 2 )
2985  return 0;
2986 
2987  // reserve object numbers for all outline items
2988  for( i = 0; i < nItems; ++i )
2989  m_aOutline[i].m_nObject = createObject();
2990 
2991  // update all parent, next and prev object ids
2992  for( i = 0; i < nItems; ++i )
2993  {
2994  PDFOutlineEntry& rItem = m_aOutline[i];
2995  int nChildren = rItem.m_aChildren.size();
2996 
2997  if( nChildren )
2998  {
2999  for( int n = 0; n < nChildren; ++n )
3000  {
3001  PDFOutlineEntry& rChild = m_aOutline[ rItem.m_aChildren[n] ];
3002 
3003  rChild.m_nParentObject = rItem.m_nObject;
3004  rChild.m_nPrevObject = (n > 0) ? m_aOutline[ rItem.m_aChildren[n-1] ].m_nObject : 0;
3005  rChild.m_nNextObject = (n < nChildren-1) ? m_aOutline[ rItem.m_aChildren[n+1] ].m_nObject : 0;
3006  }
3007 
3008  }
3009  }
3010 
3011  // calculate Count entries for all items
3012  std::vector< sal_Int32 > aCounts( nItems );
3013  updateOutlineItemCount( aCounts, 0, 0 );
3014 
3015  // emit hierarchy
3016  for( i = 0; i < nItems; ++i )
3017  {
3018  PDFOutlineEntry& rItem = m_aOutline[i];
3019  OStringBuffer aLine( 1024 );
3020 
3021  CHECK_RETURN( updateObject( rItem.m_nObject ) );
3022  aLine.append( rItem.m_nObject );
3023  aLine.append( " 0 obj\n" );
3024  aLine.append( "<<" );
3025  // number of visible children (all levels)
3026  if( i > 0 || aCounts[0] > 0 )
3027  {
3028  aLine.append( "/Count " );
3029  aLine.append( aCounts[i] );
3030  }
3031  if( ! rItem.m_aChildren.empty() )
3032  {
3033  // children list: First, Last
3034  aLine.append( "/First " );
3035  aLine.append( m_aOutline[rItem.m_aChildren.front()].m_nObject );
3036  aLine.append( " 0 R/Last " );
3037  aLine.append( m_aOutline[rItem.m_aChildren.back()].m_nObject );
3038  aLine.append( " 0 R\n" );
3039  }
3040  if( i > 0 )
3041  {
3042  // Title, Dest, Parent, Prev, Next
3043  aLine.append( "/Title" );
3044  appendUnicodeTextStringEncrypt( rItem.m_aTitle, rItem.m_nObject, aLine );
3045  aLine.append( "\n" );
3046  // Dest is not required
3047  if( rItem.m_nDestID >= 0 && rItem.m_nDestID < static_cast<sal_Int32>(m_aDests.size()) )
3048  {
3049  aLine.append( "/Dest" );
3050  appendDest( rItem.m_nDestID, aLine );
3051  }
3052  aLine.append( "/Parent " );
3053  aLine.append( rItem.m_nParentObject );
3054  aLine.append( " 0 R" );
3055  if( rItem.m_nPrevObject )
3056  {
3057  aLine.append( "/Prev " );
3058  aLine.append( rItem.m_nPrevObject );
3059  aLine.append( " 0 R" );
3060  }
3061  if( rItem.m_nNextObject )
3062  {
3063  aLine.append( "/Next " );
3064  aLine.append( rItem.m_nNextObject );
3065  aLine.append( " 0 R" );
3066  }
3067  }
3068  aLine.append( ">>\nendobj\n\n" );
3069  CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3070  }
3071 
3072  return m_aOutline[0].m_nObject;
3073 }
3074 
3075 #undef CHECK_RETURN
3076 #define CHECK_RETURN( x ) if( !x ) return false
3077 
3078 bool PDFWriterImpl::appendDest( sal_Int32 nDestID, OStringBuffer& rBuffer )
3079 {
3080  if( nDestID < 0 || nDestID >= static_cast<sal_Int32>(m_aDests.size()) )
3081  {
3082  SAL_INFO("vcl.pdfwriter", "ERROR: invalid dest " << static_cast<int>(nDestID) << " requested");
3083  return false;
3084  }
3085 
3086  const PDFDest& rDest = m_aDests[ nDestID ];
3087  const PDFPage& rDestPage = m_aPages[ rDest.m_nPage ];
3088 
3089  rBuffer.append( '[' );
3090  rBuffer.append( rDestPage.m_nPageObject );
3091  rBuffer.append( " 0 R" );
3092 
3093  switch( rDest.m_eType )
3094  {
3096  default:
3097  rBuffer.append( "/XYZ " );
3098  appendFixedInt( rDest.m_aRect.Left(), rBuffer );
3099  rBuffer.append( ' ' );
3100  appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
3101  rBuffer.append( " 0" );
3102  break;
3104  rBuffer.append( "/FitR " );
3105  appendFixedInt( rDest.m_aRect.Left(), rBuffer );
3106  rBuffer.append( ' ' );
3107  appendFixedInt( rDest.m_aRect.Top(), rBuffer );
3108  rBuffer.append( ' ' );
3109  appendFixedInt( rDest.m_aRect.Right(), rBuffer );
3110  rBuffer.append( ' ' );
3111  appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
3112  break;
3113  }
3114  rBuffer.append( ']' );
3115 
3116  return true;
3117 }
3118 
3120 {
3121  int nAnnots = m_aScreens.size();
3122  for (int i = 0; i < nAnnots; i++)
3123  {
3124  const PDFScreen& rScreen = m_aScreens[i];
3125 
3126  OStringBuffer aLine;
3127  bool bEmbed = false;
3128  if (!rScreen.m_aTempFileURL.isEmpty())
3129  {
3130  bEmbed = true;
3131  if (!updateObject(rScreen.m_nTempFileObject))
3132  continue;
3133 
3134  SvFileStream aFileStream(rScreen.m_aTempFileURL, StreamMode::READ);
3135  SvMemoryStream aMemoryStream;
3136  aMemoryStream.WriteStream(aFileStream);
3137 
3138  aLine.append(rScreen.m_nTempFileObject);
3139  aLine.append(" 0 obj\n");
3140  aLine.append("<< /Type /EmbeddedFile /Length ");
3141  aLine.append(static_cast<sal_Int64>(aMemoryStream.GetSize()));
3142  aLine.append(" >>\nstream\n");
3143  CHECK_RETURN(writeBuffer(aLine.getStr(), aLine.getLength()));
3144  aLine.setLength(0);
3145 
3146  CHECK_RETURN(writeBuffer(aMemoryStream.GetData(), aMemoryStream.GetSize()));
3147 
3148  aLine.append("\nendstream\nendobj\n\n");
3149  CHECK_RETURN(writeBuffer(aLine.getStr(), aLine.getLength()));
3150  aLine.setLength(0);
3151  }
3152 
3153  if (!updateObject(rScreen.m_nObject))
3154  continue;
3155 
3156  // Annot dictionary.
3157  aLine.append(rScreen.m_nObject);
3158  aLine.append(" 0 obj\n");
3159  aLine.append("<</Type/Annot");
3160  aLine.append("/Subtype/Screen/Rect[");
3161  appendFixedInt(rScreen.m_aRect.Left(), aLine);
3162  aLine.append(' ');
3163  appendFixedInt(rScreen.m_aRect.Top(), aLine);
3164  aLine.append(' ');
3165  appendFixedInt(rScreen.m_aRect.Right(), aLine);
3166  aLine.append(' ');
3167  appendFixedInt(rScreen.m_aRect.Bottom(), aLine);
3168  aLine.append("]");
3169 
3170  // Action dictionary.
3171  aLine.append("/A<</Type/Action /S/Rendition /AN ");
3172  aLine.append(rScreen.m_nObject);
3173  aLine.append(" 0 R ");
3174 
3175  // Rendition dictionary.
3176  aLine.append("/R<</Type/Rendition /S/MR ");
3177 
3178  // MediaClip dictionary.
3179  aLine.append("/C<</Type/MediaClip /S/MCD ");
3180  if (bEmbed)
3181  {
3182  aLine.append("/D << /Type /Filespec /F (<embedded file>) /EF << /F ");
3183  aLine.append(rScreen.m_nTempFileObject);
3184  aLine.append(" 0 R >> >>");
3185  }
3186  else
3187  {
3188  // Linked.
3189  aLine.append("/D << /Type /Filespec /FS /URL /F ");
3190  appendLiteralStringEncrypt(rScreen.m_aURL, rScreen.m_nObject, aLine, osl_getThreadTextEncoding());
3191  aLine.append(" >>");
3192  }
3193  // Allow playing the video via a tempfile.
3194  aLine.append("/P <</TF (TEMPACCESS)>>");
3195  // Until the real MIME type (instead of application/vnd.sun.star.media) is available here.
3196  aLine.append("/CT (video/mp4)");
3197  aLine.append(">>");
3198 
3199  // End Rendition dictionary by requesting play/pause/stop controls.
3200  aLine.append("/P<</BE<</C true >>>>");
3201  aLine.append(">>");
3202 
3203  // End Action dictionary.
3204  aLine.append("/OP 0 >>");
3205 
3206  // End Annot dictionary.
3207  aLine.append("/P ");
3208  aLine.append(m_aPages[rScreen.m_nPage].m_nPageObject);
3209  aLine.append(" 0 R\n>>\nendobj\n\n");
3210  CHECK_RETURN(writeBuffer(aLine.getStr(), aLine.getLength()));
3211  }
3212 
3213  return true;
3214 }
3215 
3217 {
3218  MARK("PDFWriterImpl::emitLinkAnnotations");
3219  int nAnnots = m_aLinks.size();
3220  for( int i = 0; i < nAnnots; i++ )
3221  {
3222  const PDFLink& rLink = m_aLinks[i];
3223  if( ! updateObject( rLink.m_nObject ) )
3224  continue;
3225 
3226  OStringBuffer aLine( 1024 );
3227  aLine.append( rLink.m_nObject );
3228  aLine.append( " 0 obj\n" );
3229 // i59651: key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should'
3230 // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3
3231  aLine.append( "<</Type/Annot" );
3233  aLine.append( "/F 4" );
3234  aLine.append( "/Subtype/Link/Border[0 0 0]/Rect[" );
3235 
3236  appendFixedInt( rLink.m_aRect.Left()-7, aLine );//the +7 to have a better shape of the border rectangle
3237  aLine.append( ' ' );
3238  appendFixedInt( rLink.m_aRect.Top(), aLine );
3239  aLine.append( ' ' );
3240  appendFixedInt( rLink.m_aRect.Right()+7, aLine );//the +7 to have a better shape of the border rectangle
3241  aLine.append( ' ' );
3242  appendFixedInt( rLink.m_aRect.Bottom(), aLine );
3243  aLine.append( "]" );
3244  if( rLink.m_nDest >= 0 )
3245  {
3246  aLine.append( "/Dest" );
3247  appendDest( rLink.m_nDest, aLine );
3248  }
3249  else
3250  {
3251 /*
3252 destination is external to the document, so
3253 we check in the following sequence:
3254 
3255  if target type is neither .pdf, nor .od[tpgs], then
3256  check if relative or absolute and act accordingly (use URI or 'launch application' as requested)
3257  end processing
3258  else if target is .od[tpgs]: then
3259  if conversion of type from od[tpgs] to pdf is requested, convert it and this becomes the new target file
3260  processing continue
3261 
3262  if (new)target is .pdf : then
3263  if GotToR is requested, then
3264  convert the target in GoToR where the fragment of the URI is
3265  considered the named destination in the target file, set relative or absolute as requested
3266  else strip the fragment from URL and then set URI or 'launch application' as requested
3267 */
3268 
3269 // FIXME: check if the decode mechanisms for URL processing throughout this implementation
3270 // are the correct one!!
3271 
3272 // extract target file type
3273  auto url(URIHelper::resolveIdnaHost(rLink.m_aURL));
3274 
3275  INetURLObject aDocumentURL( m_aContext.BaseURL );
3276  INetURLObject aTargetURL( url );
3277  bool bSetGoToRMode = false;
3278  bool bTargetHasPDFExtension = false;
3279  INetProtocol eTargetProtocol = aTargetURL.GetProtocol();
3280  bool bIsUNCPath = false;
3281 
3282  // check if the protocol is a known one, or if there is no protocol at all (on target only)
3283  // if there is no protocol, make the target relative to the current document directory
3284  // getting the needed URL information from the current document path
3285  if( eTargetProtocol == INetProtocol::NotValid )
3286  {
3287  if( url.getLength() > 4 && url.startsWith("\\\\\\\\"))
3288  {
3289  bIsUNCPath = true;
3290  }
3291  else
3292  {
3293  INetURLObject aNewBase( aDocumentURL );//duplicate document URL
3294  aNewBase.removeSegment(); //remove last segment from it, obtaining the base URL of the
3295  //target document
3296  aNewBase.insertName( url );
3297  aTargetURL = aNewBase;//reassign the new target URL
3298  //recompute the target protocol, with the new URL
3299  //normal URL processing resumes
3300  eTargetProtocol = aTargetURL.GetProtocol();
3301  }
3302  }
3303 
3304  OUString aFileExtension = aTargetURL.GetFileExtension();
3305 
3306  // Check if the URL ends in '/': if yes it's a directory,
3307  // it will be forced to a URI link.
3308  // possibly a malformed URI, leave it as it is, force as URI
3309  if( aTargetURL.hasFinalSlash() )
3311 
3312  if( !aFileExtension.isEmpty() )
3313  {
3315  {
3316  bool bChangeFileExtensionToPDF = false;
3317  //examine the file type (.odm .odt. .odp, odg, ods)
3318  if( aFileExtension.equalsIgnoreAsciiCase( "odm" ) )
3319  bChangeFileExtensionToPDF = true;
3320  if( aFileExtension.equalsIgnoreAsciiCase( "odt" ) )
3321  bChangeFileExtensionToPDF = true;
3322  else if( aFileExtension.equalsIgnoreAsciiCase( "odp" ) )
3323  bChangeFileExtensionToPDF = true;
3324  else if( aFileExtension.equalsIgnoreAsciiCase( "odg" ) )
3325  bChangeFileExtensionToPDF = true;
3326  else if( aFileExtension.equalsIgnoreAsciiCase( "ods" ) )
3327  bChangeFileExtensionToPDF = true;
3328  if( bChangeFileExtensionToPDF )
3329  aTargetURL.setExtension("pdf" );
3330  }
3331  //check if extension is pdf, see if GoToR should be forced
3332  bTargetHasPDFExtension = aTargetURL.GetFileExtension().equalsIgnoreAsciiCase( "pdf" );
3333  if( m_aContext.ForcePDFAction && bTargetHasPDFExtension )
3334  bSetGoToRMode = true;
3335  }
3336  //prepare the URL, if relative or not
3337  INetProtocol eBaseProtocol = aDocumentURL.GetProtocol();
3338  //queue the string common to all types of actions
3339  aLine.append( "/A<</Type/Action/S");
3340  if( bIsUNCPath ) // handle Win UNC paths
3341  {
3342  aLine.append( "/Launch/Win<</F" );
3343  // INetURLObject is not good with UNC paths, use original path
3344  appendLiteralStringEncrypt( url, rLink.m_nObject, aLine, osl_getThreadTextEncoding() );
3345  aLine.append( ">>" );
3346  }
3347  else
3348  {
3349  bool bSetRelative = false;
3350  bool bFileSpec = false;
3351  //check if relative file link is requested and if the protocol is 'file://'
3352  if( m_aContext.RelFsys && eBaseProtocol == eTargetProtocol && eTargetProtocol == INetProtocol::File )
3353  bSetRelative = true;
3354 
3355  OUString aFragment = aTargetURL.GetMark( INetURLObject::DecodeMechanism::NONE /*DecodeMechanism::WithCharset*/ ); //fragment as is,
3356  if( !bSetGoToRMode )
3357  {
3358  switch( m_aContext.DefaultLinkAction )
3359  {
3360  default:
3361  case PDFWriter::URIAction :
3363  aLine.append( "/URI/URI" );
3364  break;
3366  // now:
3367  // if a launch action is requested and the hyperlink target has a fragment
3368  // and the target file does not have a pdf extension, or it's not a 'file:://'
3369  // protocol then force the uri action on it
3370  // This code will permit the correct opening of application on web pages,
3371  // the one that normally have fragments (but I may be wrong...)
3372  // and will force the use of URI when the protocol is not file:
3373  if( (!aFragment.isEmpty() && !bTargetHasPDFExtension) ||
3374  eTargetProtocol != INetProtocol::File )
3375  {
3376  aLine.append( "/URI/URI" );
3377  }
3378  else
3379  {
3380  aLine.append( "/Launch/F" );
3381  bFileSpec = true;
3382  }
3383  break;
3384  }
3385  }
3386 
3387  //fragment are encoded in the same way as in the named destination processing
3388  if( bSetGoToRMode )
3389  {
3390  //add the fragment
3391  OUString aURLNoMark = aTargetURL.GetURLNoMark( INetURLObject::DecodeMechanism::WithCharset );
3392  aLine.append("/GoToR");
3393  aLine.append("/F");
3397  aURLNoMark, rLink.m_nObject, aLine, osl_getThreadTextEncoding() );
3398  if( !aFragment.isEmpty() )
3399  {
3400  aLine.append("/D/");
3401  appendDestinationName( aFragment , aLine );
3402  }
3403  }
3404  else
3405  {
3406  // change the fragment to accommodate the bookmark (only if the file extension
3407  // is PDF and the requested action is of the correct type)
3409  bTargetHasPDFExtension && !aFragment.isEmpty() )
3410  {
3411  OStringBuffer aLineLoc( 1024 );
3412  appendDestinationName( aFragment , aLineLoc );
3413  //substitute the fragment
3414  aTargetURL.SetMark( OStringToOUString(aLineLoc.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US) );
3415  }
3419  bFileSpec ? INetURLObject::DecodeMechanism::WithCharset : INetURLObject::DecodeMechanism::NONE
3420  ) :
3421  aURL , rLink.m_nObject, aLine, osl_getThreadTextEncoding() );
3422  }
3423  }
3424  aLine.append( ">>\n" );
3425  }
3426  if( rLink.m_nStructParent > 0 )
3427  {
3428  aLine.append( "/StructParent " );
3429  aLine.append( rLink.m_nStructParent );
3430  }
3431  aLine.append( ">>\nendobj\n\n" );
3432  CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3433  }
3434 
3435  return true;
3436 }
3437 
3438 namespace
3439 {
3440 
3441 void appendAnnotationRect(tools::Rectangle const & rRectangle, OStringBuffer & aLine)
3442 {
3443  aLine.append("/Rect[");
3444  appendFixedInt(rRectangle.Left(), aLine);
3445  aLine.append(' ');
3446  appendFixedInt(rRectangle.Top(), aLine);
3447  aLine.append(' ');
3448  appendFixedInt(rRectangle.Right(), aLine);
3449  aLine.append(' ');
3450  appendFixedInt(rRectangle.Bottom(), aLine);
3451  aLine.append("] ");
3452 }
3453 
3454 void appendObjectID(sal_Int32 nObjectID, OStringBuffer & aLine)
3455 {
3456  aLine.append(nObjectID);
3457  aLine.append(" 0 obj\n");
3458 }
3459 
3460 void appendObjectReference(sal_Int32 nObjectID, OStringBuffer & aLine)
3461 {
3462  aLine.append(nObjectID);
3463  aLine.append(" 0 R ");
3464 }
3465 
3466 } // end anonymous namespace
3467 
3468 void PDFWriterImpl::emitTextAnnotationLine(OStringBuffer & aLine, PDFNoteEntry const & rNote)
3469 {
3470  appendObjectID(rNote.m_nObject, aLine);
3471 
3472  aLine.append("<</Type /Annot /Subtype /Text ");
3473 
3474 // i59651: key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should'
3475 // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3
3477  aLine.append("/F 4 ");
3478 
3479  appendAnnotationRect(rNote.m_aRect, aLine);
3480 
3481  aLine.append("/Popup ");
3482  appendObjectReference(rNote.m_aPopUpAnnotation.m_nObject, aLine);
3483 
3484  auto & rDateTime = rNote.m_aContents.maModificationDate;
3485 
3486  aLine.append("/M ");
3487  appendPdfTimeDate(aLine, rDateTime.Year, rDateTime.Month, rDateTime.Day, rDateTime.Hours, rDateTime.Minutes, rDateTime.Seconds, 0);
3488 
3489  // contents of the note (type text string)
3490  aLine.append("/Contents ");
3492  aLine.append("\n");
3493 
3494  // optional title
3495  if (!rNote.m_aContents.Title.isEmpty())
3496  {
3497  aLine.append("/T ");
3499  aLine.append("\n");
3500  }
3501  aLine.append(">>\n");
3502  aLine.append("endobj\n\n");
3503 }
3504 
3505 void PDFWriterImpl::emitPopupAnnotationLine(OStringBuffer & aLine, PDFPopupAnnotation const & rPopUp)
3506 {
3507  appendObjectID(rPopUp.m_nObject, aLine);
3508  aLine.append("<</Type /Annot /Subtype /Popup ");
3509  aLine.append("/Parent ");
3510  appendObjectReference(rPopUp.m_nParentObject, aLine);
3511  aLine.append(">>\n");
3512  aLine.append("endobj\n\n");
3513 }
3514 
3516 {
3517  // emit note annotations
3518  int nAnnots = m_aNotes.size();
3519  for( int i = 0; i < nAnnots; i++ )
3520  {
3521  const PDFNoteEntry& rNote = m_aNotes[i];
3522  const PDFPopupAnnotation& rPopUp = rNote.m_aPopUpAnnotation;
3523 
3524  {
3525  if (!updateObject(rNote.m_nObject))
3526  return false;
3527 
3528  OStringBuffer aLine(1024);
3529 
3530  emitTextAnnotationLine(aLine, rNote);
3531 
3532  if (!writeBuffer(aLine.getStr(), aLine.getLength()))
3533  return false;
3534  }
3535 
3536  {
3537 
3538  if (!updateObject(rPopUp.m_nObject))
3539  return false;
3540 
3541  OStringBuffer aLine(1024);
3542 
3543  emitPopupAnnotationLine(aLine, rPopUp);
3544 
3545  if (!writeBuffer(aLine.getStr(), aLine.getLength()))
3546  return false;
3547  }
3548  }
3549  return true;
3550 }
3551 
3552 Font PDFWriterImpl::replaceFont( const vcl::Font& rControlFont, const vcl::Font& rAppSetFont )
3553 {
3554  bool bAdjustSize = false;
3555 
3556  Font aFont( rControlFont );
3557  if( aFont.GetFamilyName().isEmpty() )
3558  {
3559  aFont = rAppSetFont;
3560  if( rControlFont.GetFontHeight() )
3561  aFont.SetFontSize( Size( 0, rControlFont.GetFontHeight() ) );
3562  else
3563  bAdjustSize = true;
3564  if( rControlFont.GetItalic() != ITALIC_DONTKNOW )
3565  aFont.SetItalic( rControlFont.GetItalic() );
3566  if( rControlFont.GetWeight() != WEIGHT_DONTKNOW )
3567  aFont.SetWeight( rControlFont.GetWeight() );
3568  }
3569  else if( ! aFont.GetFontHeight() )
3570  {
3571  aFont.SetFontSize( rAppSetFont.GetFontSize() );
3572  bAdjustSize = true;
3573  }
3574  if( bAdjustSize )
3575  {
3576  Size aFontSize = aFont.GetFontSize();
3578  aFontSize = OutputDevice::LogicToLogic( aFontSize, pDefDev->GetMapMode(), getMapMode() );
3579  aFont.SetFontSize( aFontSize );
3580  }
3581  return aFont;
3582 }
3583 
3585 {
3586  sal_Int32 nBest = 4; // default to Helvetica
3587  OUString aFontName( rFont.GetFamilyName() );
3588  aFontName = aFontName.toAsciiLowerCase();
3589 
3590  if( aFontName.indexOf( "times" ) != -1 )
3591  nBest = 8;
3592  else if( aFontName.indexOf( "courier" ) != -1 )
3593  nBest = 0;
3594  else if( aFontName.indexOf( "dingbats" ) != -1 )
3595  nBest = 13;
3596  else if( aFontName.indexOf( "symbol" ) != -1 )
3597  nBest = 12;
3598  if( nBest < 12 )
3599  {
3600  if( rFont.GetItalic() == ITALIC_OBLIQUE || rFont.GetItalic() == ITALIC_NORMAL )
3601  nBest += 1;
3602  if( rFont.GetWeight() > WEIGHT_MEDIUM )
3603  nBest += 2;
3604  }
3605 
3606  if( m_aBuildinFontToObjectMap.find( nBest ) == m_aBuildinFontToObjectMap.end() )
3608 
3609  return nBest;
3610 }
3611 
3612 static const Color& replaceColor( const Color& rCol1, const Color& rCol2 )
3613 {
3614  return (rCol1 == COL_TRANSPARENT) ? rCol2 : rCol1;
3615 }
3616 
3618 {
3620 
3621  // save graphics state
3622  push( PushFlags::ALL );
3623 
3624  // transform relative to control's coordinates since an
3625  // appearance stream is a form XObject
3626  // this relies on the m_aRect member of rButton NOT already being transformed
3627  // to default user space
3628  if( rWidget.Background || rWidget.Border )
3629  {
3630  setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetLightColor() ) : COL_TRANSPARENT );
3631  setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetDialogColor() ) : COL_TRANSPARENT );
3632  drawRectangle( rWidget.Location );
3633  }
3634  // prepare font to use
3635  Font aFont = replaceFont( rWidget.TextFont, rSettings.GetPushButtonFont() );
3636  setFont( aFont );
3637  setTextColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ) );
3638 
3639  drawText( rButton.m_aRect, rButton.m_aText, rButton.m_nTextStyle );
3640 
3641  // create DA string while local mapmode is still in place
3642  // (that is before endRedirect())
3643  OStringBuffer aDA( 256 );
3644  appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ), aDA );
3645  Font aDummyFont( "Helvetica", aFont.GetFontSize() );
3646  sal_Int32 nDummyBuildin = getBestBuildinFont( aDummyFont );
3647  aDA.append( ' ' );
3648  aDA.append(pdf::BuildinFontFace::Get(nDummyBuildin).getNameObject());
3649  aDA.append( ' ' );
3650  m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aFont.GetFontHeight() ), aDA );
3651  aDA.append( " Tf" );
3652  rButton.m_aDAString = aDA.makeStringAndClear();
3653 
3654  pop();
3655 
3656  rButton.m_aAppearances[ "N" ][ "Standard" ] = new SvMemoryStream();
3657 
3658  /* seems like a bad hack but at least works in both AR5 and 6:
3659  we draw the button ourselves and tell AR
3660  the button would be totally transparent with no text
3661 
3662  One would expect that simply setting a normal appearance
3663  should suffice, but no, as soon as the user actually presses
3664  the button and an action is tied to it (gasp! a button that
3665  does something) the appearance gets replaced by some crap that AR
3666  creates on the fly even if no DA or MK is given. On AR6 at least
3667  the DA and MK work as expected, but on AR5 this creates a region
3668  filled with the background color but nor text. Urgh.
3669  */
3670  rButton.m_aMKDict = "/BC [] /BG [] /CA";
3671  rButton.m_aMKDictCAString = "";
3672 }
3673 
3675  const PDFWriter::AnyWidget& rWidget,
3676  const StyleSettings& rSettings )
3677 {
3678  Font aFont = replaceFont( rWidget.TextFont, rSettings.GetFieldFont() );
3679 
3680  if( rWidget.Background || rWidget.Border )
3681  {
3682  if( rWidget.Border && rWidget.BorderColor == COL_TRANSPARENT )
3683  {
3684  sal_Int32 nDelta = GetDPIX() / 500;
3685  if( nDelta < 1 )
3686  nDelta = 1;
3688  tools::Rectangle aRect = rIntern.m_aRect;
3689  setFillColor( rSettings.GetLightBorderColor() );
3690  drawRectangle( aRect );
3691  aRect.AdjustLeft(nDelta ); aRect.AdjustTop(nDelta );
3692  aRect.AdjustRight( -nDelta ); aRect.AdjustBottom( -nDelta );
3693  setFillColor( rSettings.GetFieldColor() );
3694  drawRectangle( aRect );
3695  setFillColor( rSettings.GetLightColor() );
3696  drawRectangle( tools::Rectangle( Point( aRect.Left(), aRect.Bottom()-nDelta ), aRect.BottomRight() ) );
3697  drawRectangle( tools::Rectangle( Point( aRect.Right()-nDelta, aRect.Top() ), aRect.BottomRight() ) );
3698  setFillColor( rSettings.GetDarkShadowColor() );
3699  drawRectangle( tools::Rectangle( aRect.TopLeft(), Point( aRect.Left()+nDelta, aRect.Bottom() ) ) );
3700  drawRectangle( tools::Rectangle( aRect.TopLeft(), Point( aRect.Right(), aRect.Top()+nDelta ) ) );
3701  }
3702  else
3703  {
3704  setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetShadowColor() ) : COL_TRANSPARENT );
3705  setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : COL_TRANSPARENT );
3706  drawRectangle( rIntern.m_aRect );
3707  }
3708 
3709  if( rWidget.Border )
3710  {
3711  // adjust edit area accounting for border
3712  sal_Int32 nDelta = aFont.GetFontHeight()/4;
3713  if( nDelta < 1 )
3714  nDelta = 1;
3715  rIntern.m_aRect.AdjustLeft(nDelta );
3716  rIntern.m_aRect.AdjustTop(nDelta );
3717  rIntern.m_aRect.AdjustRight( -nDelta );
3718  rIntern.m_aRect.AdjustBottom( -nDelta );
3719  }
3720  }
3721  return aFont;
3722 }
3723 
3725 {
3727  SvMemoryStream* pEditStream = new SvMemoryStream( 1024, 1024 );
3728 
3729  push( PushFlags::ALL );
3730 
3731  // prepare font to use, draw field border
3732  Font aFont = drawFieldBorder( rEdit, rWidget, rSettings );
3733  sal_Int32 nBest = getSystemFont( aFont );
3734 
3735  // prepare DA string
3736  OStringBuffer aDA( 32 );
3737  appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA );
3738  aDA.append( ' ' );
3739  aDA.append( "/F" );
3740  aDA.append( nBest );
3741 
3742  OStringBuffer aDR( 32 );
3743  aDR.append( "/Font " );
3744  aDR.append( getFontDictObject() );
3745  aDR.append( " 0 R" );
3746  rEdit.m_aDRDict = aDR.makeStringAndClear();
3747  aDA.append( ' ' );
3748  m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetFontHeight() ), aDA );
3749  aDA.append( " Tf" );
3750 
3751  /* create an empty appearance stream, let the viewer create
3752  the appearance at runtime. This is because AR5 seems to
3753  paint the widget appearance always, and a dynamically created
3754  appearance on top of it. AR6 is well behaved in that regard, so
3755  that behaviour seems to be a bug. Anyway this empty appearance
3756  relies on /NeedAppearances in the AcroForm dictionary set to "true"
3757  */
3758  beginRedirect( pEditStream, rEdit.m_aRect );
3759  OString aAppearance = "/Tx BMC\nEMC\n";
3760  writeBuffer( aAppearance.getStr(), aAppearance.getLength() );
3761 
3762  endRedirect();
3763  pop();
3764 
3765  rEdit.m_aAppearances[ "N" ][ "Standard" ] = pEditStream;
3766 
3767  rEdit.m_aDAString = aDA.makeStringAndClear();
3768 }
3769 
3771 {
3773  SvMemoryStream* pListBoxStream = new SvMemoryStream( 1024, 1024 );
3774 
3775  push( PushFlags::ALL );
3776 
3777  // prepare font to use, draw field border
3778  Font aFont = drawFieldBorder( rBox, rWidget, rSettings );
3779  sal_Int32 nBest = getSystemFont( aFont );
3780 
3781  beginRedirect( pListBoxStream, rBox.m_aRect );
3782 
3784  setFillColor( replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) );
3785  drawRectangle( rBox.m_aRect );
3786 
3787  // empty appearance, see createDefaultEditAppearance for reference
3788  OString aAppearance = "/Tx BMC\nEMC\n";
3789  writeBuffer( aAppearance.getStr(), aAppearance.getLength() );
3790 
3791  endRedirect();
3792  pop();
3793 
3794  rBox.m_aAppearances[ "N" ][ "Standard" ] = pListBoxStream;
3795 
3796  // prepare DA string
3797  OStringBuffer aDA( 256 );
3798  // prepare DA string
3799  appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA );
3800  aDA.append( ' ' );
3801  aDA.append( "/F" );
3802  aDA.append( nBest );
3803 
3804  OStringBuffer aDR( 32 );
3805  aDR.append( "/Font " );
3806  aDR.append( getFontDictObject() );
3807  aDR.append( " 0 R" );
3808  rBox.m_aDRDict = aDR.makeStringAndClear();
3809  aDA.append( ' ' );
3810  m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetFontHeight() ), aDA );
3811  aDA.append( " Tf" );
3812  rBox.m_aDAString = aDA.makeStringAndClear();
3813 }
3814 
3816 {
3818 
3819  // save graphics state
3820  push( PushFlags::ALL );
3821 
3822  if( rWidget.Background || rWidget.Border )
3823  {
3824  setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : COL_TRANSPARENT );
3825  setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : COL_TRANSPARENT );
3826  drawRectangle( rBox.m_aRect );
3827  }
3828 
3829  Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() );
3830  setFont( aFont );
3831  Size aFontSize = aFont.GetFontSize();
3832  if( aFontSize.Height() > rBox.m_aRect.GetHeight() )
3833  aFontSize.setHeight( rBox.m_aRect.GetHeight() );
3834  sal_Int32 nDelta = aFontSize.Height()/10;
3835  if( nDelta < 1 )
3836  nDelta = 1;
3837 
3838  tools::Rectangle aCheckRect, aTextRect;
3839  {
3840  aCheckRect.SetLeft( rBox.m_aRect.Left() + nDelta );
3841  aCheckRect.SetTop( rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2 );
3842  aCheckRect.SetRight( aCheckRect.Left() + aFontSize.Height() );
3843  aCheckRect.SetBottom( aCheckRect.Top() + aFontSize.Height() );
3844 
3845  // #i74206# handle small controls without text area
3846  while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
3847  {
3848  aCheckRect.AdjustRight( -nDelta );
3849  aCheckRect.AdjustTop(nDelta/2 );
3850  aCheckRect.AdjustBottom( -(nDelta - (nDelta/2)) );
3851  }
3852 
3853  aTextRect.SetLeft( rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta );
3854  aTextRect.SetTop( rBox.m_aRect.Top() );
3855  aTextRect.SetRight( aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta );
3856  aTextRect.SetBottom( rBox.m_aRect.Bottom() );
3857  }
3860  OStringBuffer aLW( 32 );
3861  aLW.append( "q " );
3862  m_aPages[m_nCurrentPage].appendMappedLength( nDelta, aLW );
3863  aLW.append( " w " );
3864  writeBuffer( aLW.getStr(), aLW.getLength() );
3865  drawRectangle( aCheckRect );
3866  writeBuffer( " Q\n", 3 );
3867  setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
3868  drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle );
3869 
3870  pop();
3871 
3872  OStringBuffer aDA( 256 );
3873 
3874  // tdf#93853 don't rely on Zapf (or any other 'standard' font)
3875  // being present, but our own OpenSymbol - N.B. PDF/A for good
3876  // reasons require even the standard PS fonts to be embedded!
3877  Push();
3878  SetFont( Font( OUString( "OpenSymbol" ), aFont.GetFontSize() ) );
3879  FontCharMapRef pMap;
3880  GetFontCharMap(pMap);
3881  const LogicalFontInstance* pFontInstance = GetFontInstance();
3882  const PhysicalFontFace* pDevFont = pFontInstance->GetFontFace();
3883  Pop();
3884 
3885  // make sure OpenSymbol is embedded, and includes our checkmark
3886  const sal_Unicode cMark=0x2713;
3887  const GlyphItem aItem(0, 0, pMap->GetGlyphIndex(cMark),
3888  Point(), GlyphItemFlags::NONE, 0, 0,
3889  const_cast<LogicalFontInstance*>(pFontInstance));
3890  const std::vector<sal_Ucs> aCodeUnits={ cMark };
3891  sal_uInt8 nMappedGlyph;
3892  sal_Int32 nMappedFontObject;
3893  registerGlyph(&aItem, pDevFont, aCodeUnits, nMappedGlyph, nMappedFontObject);
3894 
3895  appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
3896  aDA.append( ' ' );
3897  aDA.append( "/F" );
3898  aDA.append( nMappedFontObject );
3899  aDA.append( " 0 Tf" );
3900 
3901  OStringBuffer aDR( 32 );
3902  aDR.append( "/Font " );
3903  aDR.append( getFontDictObject() );
3904  aDR.append( " 0 R" );
3905  rBox.m_aDRDict = aDR.makeStringAndClear();
3906  rBox.m_aDAString = aDA.makeStringAndClear();
3907  rBox.m_aMKDict = "/CA";
3908  rBox.m_aMKDictCAString = "8";
3909  rBox.m_aRect = aCheckRect;
3910 
3911  // create appearance streams
3912  sal_Int32 nCharXOffset = 1000 - 787; // metrics from OpenSymbol
3913  nCharXOffset *= aCheckRect.GetHeight();
3914  nCharXOffset /= 2000;
3915  sal_Int32 nCharYOffset = 1000 - (820-143); // metrics from Zapf
3916  nCharYOffset *= aCheckRect.GetHeight();
3917  nCharYOffset /= 2000;
3918 
3919  // write 'checked' appearance stream
3920  SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 );
3921  beginRedirect( pCheckStream, aCheckRect );
3922  aDA.append( "/Tx BMC\nq BT\n" );
3923  appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
3924  aDA.append( ' ' );
3925  aDA.append( "/F" );
3926  aDA.append( nMappedFontObject );
3927  aDA.append( ' ' );
3928  m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA );
3929  aDA.append( " Tf\n" );
3930  m_aPages[ m_nCurrentPage ].appendMappedLength( nCharXOffset, aDA );
3931  aDA.append( " " );
3932  m_aPages[ m_nCurrentPage ].appendMappedLength( nCharYOffset, aDA );
3933  aDA.append( " Td <" );
3934  appendHex( nMappedGlyph, aDA );
3935  aDA.append( "> Tj\nET\nQ\nEMC\n" );
3936  writeBuffer( aDA.getStr(), aDA.getLength() );
3937  endRedirect();
3938  rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream;
3939 
3940  // write 'unchecked' appearance stream
3941  SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 );
3942  beginRedirect( pUncheckStream, aCheckRect );
3943  writeBuffer( "/Tx BMC\nEMC\n", 12 );
3944  endRedirect();
3945  rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream;
3946 }
3947 
3949 {
3951 
3952  // save graphics state
3953  push( PushFlags::ALL );
3954 
3955  if( rWidget.Background || rWidget.Border )
3956  {
3957  setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : COL_TRANSPARENT );
3958  setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : COL_TRANSPARENT );
3959  drawRectangle( rBox.m_aRect );
3960  }
3961 
3962  Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() );
3963  setFont( aFont );
3964  Size aFontSize = aFont.GetFontSize();
3965  if( aFontSize.Height() > rBox.m_aRect.GetHeight() )
3966  aFontSize.setHeight( rBox.m_aRect.GetHeight() );
3967  sal_Int32 nDelta = aFontSize.Height()/10;
3968  if( nDelta < 1 )
3969  nDelta = 1;
3970 
3971  tools::Rectangle aCheckRect, aTextRect;
3972  {
3973  aCheckRect.SetLeft( rBox.m_aRect.Left() + nDelta );
3974  aCheckRect.SetTop( rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2 );
3975  aCheckRect.SetRight( aCheckRect.Left() + aFontSize.Height() );
3976  aCheckRect.SetBottom( aCheckRect.Top() + aFontSize.Height() );
3977 
3978  // #i74206# handle small controls without text area
3979  while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
3980  {
3981  aCheckRect.AdjustRight( -nDelta );
3982  aCheckRect.AdjustTop(nDelta/2 );
3983  aCheckRect.AdjustBottom( -(nDelta - (nDelta/2)) );
3984  }
3985 
3986  aTextRect.SetLeft( rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta );
3987  aTextRect.SetTop( rBox.m_aRect.Top() );
3988  aTextRect.SetRight( aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta );
3989  aTextRect.SetBottom( rBox.m_aRect.Bottom() );
3990  }
3993  OStringBuffer aLW( 32 );
3994  aLW.append( "q " );
3995  m_aPages[ m_nCurrentPage ].appendMappedLength( nDelta, aLW );
3996  aLW.append( " w " );
3997  writeBuffer( aLW.getStr(), aLW.getLength() );
3998  drawEllipse( aCheckRect );
3999  writeBuffer( " Q\n", 3 );
4000  setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
4001  drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle );
4002 
4003  pop();
4004 
4005  OStringBuffer aDA( 256 );
4006  appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
4007  rBox.m_aDAString = aDA.makeStringAndClear();
4008  //to encrypt this (el)
4009  rBox.m_aMKDict = "/CA";
4010  //after this assignment, to m_aMKDic cannot be added anything
4011  rBox.m_aMKDictCAString = "l";
4012 
4013  rBox.m_aRect = aCheckRect;
4014 
4015  // create appearance streams
4016  push( PushFlags::ALL);
4017  SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 );
4018 
4019  beginRedirect( pCheckStream, aCheckRect );
4020  aDA.append( "/Tx BMC\nq BT\n" );
4021  appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
4022  aDA.append( ' ' );
4023  m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA );
4024  aDA.append( " 0 0 Td\nET\nQ\n" );
4025  writeBuffer( aDA.getStr(), aDA.getLength() );
4026  setFillColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
4028  aCheckRect.AdjustLeft(3*nDelta );
4029  aCheckRect.AdjustTop(3*nDelta );
4030  aCheckRect.AdjustBottom( -(3*nDelta) );
4031  aCheckRect.AdjustRight( -(3*nDelta) );
4032  drawEllipse( aCheckRect );
4033  writeBuffer( "\nEMC\n", 5 );
4034  endRedirect();
4035 
4036  pop();
4037  rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream;
4038 
4039  SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 );
4040  beginRedirect( pUncheckStream, aCheckRect );
4041  writeBuffer( "/Tx BMC\nEMC\n", 12 );
4042  endRedirect();
4043  rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream;
4044 }
4045 
4046 bool PDFWriterImpl::emitAppearances( PDFWidget& rWidget, OStringBuffer& rAnnotDict )
4047 {
4048  // TODO: check and insert default streams
4049  OString aStandardAppearance;
4050  switch( rWidget.m_eType )
4051  {
4052  case PDFWriter::CheckBox:
4053  aStandardAppearance = OUStringToOString( rWidget.m_aValue, RTL_TEXTENCODING_ASCII_US );
4054  break;
4055  default:
4056  break;
4057  }
4058 
4059  if( !rWidget.m_aAppearances.empty() )
4060  {
4061  rAnnotDict.append( "/AP<<\n" );
4062  for (auto & dict_item : rWidget.m_aAppearances)
4063  {
4064  rAnnotDict.append( "/" );
4065  rAnnotDict.append( dict_item.first );
4066  bool bUseSubDict = (dict_item.second.size() > 1);
4067 
4068  // PDF/A requires sub-dicts for /FT/Btn objects (clause
4069  // 6.3.3)
4071  {
4072  if( rWidget.m_eType == PDFWriter::RadioButton ||
4073  rWidget.m_eType == PDFWriter::CheckBox ||
4074  rWidget.m_eType == PDFWriter::PushButton )
4075  {
4076  bUseSubDict = true;
4077  }
4078  }
4079 
4080  rAnnotDict.append( bUseSubDict ? "<<" : " " );
4081 
4082  for (auto const& stream_item : dict_item.second)
4083  {
4084  SvMemoryStream* pApppearanceStream = stream_item.second;
4085  dict_item.second[ stream_item.first ] = nullptr;
4086 
4087  bool bDeflate = compressStream( pApppearanceStream );
4088 
4089  sal_Int64 nStreamLen = pApppearanceStream->TellEnd();
4090  pApppearanceStream->Seek( STREAM_SEEK_TO_BEGIN );
4091  sal_Int32 nObject = createObject();
4092  CHECK_RETURN( updateObject( nObject ) );
4094  {
4095  emitComment( "PDFWriterImpl::emitAppearances" );
4096  }
4097  OStringBuffer aLine;
4098  aLine.append( nObject );
4099 
4100  aLine.append( " 0 obj\n"
4101  "<</Type/XObject\n"
4102  "/Subtype/Form\n"
4103  "/BBox[0 0 " );
4104  appendFixedInt( rWidget.m_aRect.GetWidth()-1, aLine );
4105  aLine.append( " " );
4106  appendFixedInt( rWidget.m_aRect.GetHeight()-1, aLine );
4107  aLine.append( "]\n"
4108  "/Resources " );
4109  aLine.append( getResourceDictObj() );
4110  aLine.append( " 0 R\n"
4111  "/Length " );
4112  aLine.append( nStreamLen );
4113  aLine.append( "\n" );
4114  if( bDeflate )
4115  aLine.append( "/Filter/FlateDecode\n" );
4116  aLine.append( ">>\nstream\n" );
4117  CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4118  checkAndEnableStreamEncryption( nObject );
4119  CHECK_RETURN( writeBuffer( pApppearanceStream->GetData(), nStreamLen ) );
4121  CHECK_RETURN( writeBuffer( "\nendstream\nendobj\n\n", 19 ) );
4122 
4123  if( bUseSubDict )
4124  {
4125  rAnnotDict.append( " /" );
4126  rAnnotDict.append( stream_item.first );
4127  rAnnotDict.append( " " );
4128  }
4129  rAnnotDict.append( nObject );
4130  rAnnotDict.append( " 0 R" );
4131 
4132  delete pApppearanceStream;
4133  }
4134 
4135  rAnnotDict.append( bUseSubDict ? ">>\n" : "\n" );
4136  }
4137  rAnnotDict.append( ">>\n" );
4138  if( !aStandardAppearance.isEmpty() )
4139  {
4140  rAnnotDict.append( "/AS /" );
4141  rAnnotDict.append( aStandardAppearance );
4142  rAnnotDict.append( "\n" );
4143  }
4144  }
4145 
4146  return true;
4147 }
4148 
4150 {
4152 
4153  int nAnnots = m_aWidgets.size();
4154  for( int a = 0; a < nAnnots; a++ )
4155  {
4156  PDFWidget& rWidget = m_aWidgets[a];
4157 
4158  OStringBuffer aLine( 1024 );
4159  OStringBuffer aValue( 256 );
4160  aLine.append( rWidget.m_nObject );
4161  aLine.append( " 0 obj\n"
4162  "<<" );
4163  if( rWidget.m_eType != PDFWriter::Hierarchy )
4164  {
4165  // emit widget annotation only for terminal fields
4166  if( rWidget.m_aKids.empty() )
4167  {
4168  int iRectMargin;
4169 
4170  aLine.append( "/Type/Annot/Subtype/Widget/F " );
4171 
4172  if (rWidget.m_eType == PDFWriter::Signature)
4173  {
4174  aLine.append( "132\n" ); // Print & Locked
4175  iRectMargin = 0;
4176  }
4177  else
4178  {
4179  aLine.append( "4\n" );
4180  iRectMargin = 1;
4181  }
4182 
4183  aLine.append("/Rect[" );
4184  appendFixedInt( rWidget.m_aRect.Left()-iRectMargin, aLine );
4185  aLine.append( ' ' );
4186  appendFixedInt( rWidget.m_aRect.Top()+iRectMargin, aLine );
4187  aLine.append( ' ' );
4188  appendFixedInt( rWidget.m_aRect.Right()+iRectMargin, aLine );
4189  aLine.append( ' ' );
4190  appendFixedInt( rWidget.m_aRect.Bottom()-iRectMargin, aLine );
4191  aLine.append( "]\n" );
4192  }
4193  aLine.append( "/FT/" );
4194  switch( rWidget.m_eType )
4195  {
4197  case PDFWriter::CheckBox:
4198  // for radio buttons only the RadioButton field, not the
4199  // CheckBox children should have a value, else acrobat reader
4200  // does not always check the right button
4201  // of course real check boxes (not belonging to a radio group)
4202  // need their values, too
4203  if( rWidget.m_eType == PDFWriter::RadioButton || rWidget.m_nRadioGroup < 0 )
4204  {
4205  aValue.append( "/" );
4206  // check for radio group with all buttons unpressed
4207  if( rWidget.m_aValue.isEmpty() )
4208  aValue.append( "Off" );
4209  else
4210  appendName( rWidget.m_aValue, aValue );
4211  }
4212  [[fallthrough]];
4213  case PDFWriter::PushButton:
4214  aLine.append( "Btn" );
4215  break;
4216  case PDFWriter::ListBox:
4217  if( rWidget.m_nFlags & 0x200000 ) // multiselect
4218  {
4219  aValue.append( "[" );
4220  for( size_t i = 0; i < rWidget.m_aSelectedEntries.size(); i++ )
4221  {
4222  sal_Int32 nEntry = rWidget.m_aSelectedEntries[i];
4223  if( nEntry >= 0 && nEntry < sal_Int32(rWidget.m_aListEntries.size()) )
4224  appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ nEntry ], rWidget.m_nObject, aValue );
4225  }
4226  aValue.append( "]" );
4227  }
4228  else if( !rWidget.m_aSelectedEntries.empty() &&
4229  rWidget.m_aSelectedEntries[0] >= 0 &&
4230  rWidget.m_aSelectedEntries[0] < sal_Int32(rWidget.m_aListEntries.size()) )
4231  {
4232  appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ rWidget.m_aSelectedEntries[0] ], rWidget.m_nObject, aValue );
4233  }
4234  else
4235  appendUnicodeTextStringEncrypt( OUString(), rWidget.m_nObject, aValue );
4236  aLine.append( "Ch" );
4237  break;
4238  case PDFWriter::ComboBox:
4239  appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue );
4240  aLine.append( "Ch" );
4241  break;
4242  case PDFWriter::Edit:
4243  aLine.append( "Tx" );
4244  appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue );
4245  break;
4246  case PDFWriter::Signature:
4247  aLine.append( "Sig" );
4248  aValue.append(OUStringToOString(rWidget.m_aValue, RTL_TEXTENCODING_ASCII_US));
4249  break;
4250  case PDFWriter::Hierarchy: // make the compiler happy
4251  break;
4252  }
4253  aLine.append( "\n" );
4254  aLine.append( "/P " );
4255  aLine.append( m_aPages[ rWidget.m_nPage ].m_nPageObject );
4256  aLine.append( " 0 R\n" );
4257  }
4258  if( rWidget.m_nParent )
4259  {
4260  aLine.append( "/Parent " );
4261  aLine.append( rWidget.m_nParent );
4262  aLine.append( " 0 R\n" );
4263  }
4264  if( !rWidget.m_aKids.empty() )
4265  {
4266  aLine.append( "/Kids[" );
4267  for( size_t i = 0; i < rWidget.m_aKids.size(); i++ )
4268  {
4269  aLine.append( rWidget.m_aKids[i] );
4270  aLine.append( " 0 R" );
4271  aLine.append( ( (i&15) == 15 ) ? "\n" : " " );
4272  }
4273  aLine.append( "]\n" );
4274  }
4275  if( !rWidget.m_aName.isEmpty() )
4276  {
4277  aLine.append( "/T" );
4278  appendLiteralStringEncrypt( rWidget.m_aName, rWidget.m_nObject, aLine );
4279  aLine.append( "\n" );
4280  }
4281  if( m_aContext.Version > PDFWriter::PDFVersion::PDF_1_2 && !rWidget.m_aDescription.isEmpty() )
4282  {
4283  // the alternate field name should be unicode able since it is
4284  // supposed to be used in UI
4285  aLine.append( "/TU" );
4286  appendUnicodeTextStringEncrypt( rWidget.m_aDescription, rWidget.m_nObject, aLine );
4287  aLine.append( "\n" );
4288  }
4289 
4290  if( rWidget.m_nFlags )
4291  {
4292  aLine.append( "/Ff " );
4293  aLine.append( rWidget.m_nFlags );
4294  aLine.append( "\n" );
4295  }
4296  if( !aValue.isEmpty() )
4297  {
4298  OString aVal = aValue.makeStringAndClear();
4299  aLine.append( "/V " );
4300  aLine.append( aVal );
4301  aLine.append( "\n"
4302  "/DV " );
4303  aLine.append( aVal );
4304  aLine.append( "\n" );
4305  }
4306  if( rWidget.m_eType == PDFWriter::ListBox || rWidget.m_eType == PDFWriter::ComboBox )
4307  {
4308  sal_Int32 nTI = -1;
4309  aLine.append( "/Opt[\n" );
4310  sal_Int32 i = 0;
4311  for (auto const& entry : rWidget.m_aListEntries)
4312  {
4313  appendUnicodeTextStringEncrypt( entry, rWidget.m_nObject, aLine );
4314  aLine.append( "\n" );
4315  if( entry == rWidget.m_aValue )
4316  nTI = i;
4317  ++i;
4318  }
4319  aLine.append( "]\n" );
4320  if( nTI > 0 )
4321  {
4322  aLine.append( "/TI " );
4323  aLine.append( nTI );
4324  aLine.append( "\n" );
4325  if( rWidget.m_nFlags & 0x200000 ) // Multiselect
4326  {
4327  aLine.append( "/I [" );
4328  aLine.append( nTI );
4329  aLine.append( "]\n" );
4330  }
4331  }
4332  }
4333  if( rWidget.m_eType == PDFWriter::Edit && rWidget.m_nMaxLen > 0 )
4334  {
4335  aLine.append( "/MaxLen " );
4336  aLine.append( rWidget.m_nMaxLen );
4337  aLine.append( "\n" );
4338  }
4339  if( rWidget.m_eType == PDFWriter::PushButton )
4340  {
4341  if(!m_bIsPDF_A1)
4342  {
4343  OStringBuffer aDest;
4344  if( rWidget.m_nDest != -1 && appendDest( m_aDestinationIdTranslation[ rWidget.m_nDest ], aDest ) )
4345  {
4346  aLine.append( "/AA<</D<</Type/Action/S/GoTo/D " );
4347  aLine.append( aDest.makeStringAndClear() );
4348  aLine.append( ">>>>\n" );
4349  }
4350  else if( rWidget.m_aListEntries.empty() )
4351  {
4352  if( !m_bIsPDF_A2 && !m_bIsPDF_A3 )
4353  {
4354  // create a reset form action
4355  aLine.append( "/AA<</D<</Type/Action/S/ResetForm>>>>\n" );
4356  }
4357  }
4358  else if( rWidget.m_bSubmit )
4359  {
4360  // create a submit form action
4361  aLine.append( "/AA<</D<</Type/Action/S/SubmitForm/F" );
4362  appendLiteralStringEncrypt( rWidget.m_aListEntries.front(), rWidget.m_nObject, aLine, osl_getThreadTextEncoding() );
4363  aLine.append( "/Flags " );
4364 
4365  sal_Int32 nFlags = 0;
4366  switch( m_aContext.SubmitFormat )
4367  {
4368  case PDFWriter::HTML:
4369  nFlags |= 4;
4370  break;
4371  case PDFWriter::XML:
4373  nFlags |= 32;
4374  break;
4375  case PDFWriter::PDF:
4377  nFlags |= 256;
4378  break;
4379  case PDFWriter::FDF:
4380  default:
4381  break;
4382  }
4383  if( rWidget.m_bSubmitGet )
4384  nFlags |= 8;
4385  aLine.append( nFlags );
4386  aLine.append( ">>>>\n" );
4387  }
4388  else
4389  {
4390  // create a URI action
4391  aLine.append( "/AA<</D<</Type/Action/S/URI/URI(" );
4392  aLine.append( OUStringToOString( rWidget.m_aListEntries.front(), RTL_TEXTENCODING_ASCII_US ) );
4393  aLine.append( ")>>>>\n" );
4394  }
4395  }
4396  else
4398  }
4399  if( !rWidget.m_aDAString.isEmpty() )
4400  {
4401  if( !rWidget.m_aDRDict.isEmpty() )
4402  {
4403  aLine.append( "/DR<<" );
4404  aLine.append( rWidget.m_aDRDict );
4405  aLine.append( ">>\n" );
4406  }
4407  else
4408  {
4409  aLine.append( "/DR<</Font<<" );
4410  appendBuildinFontsToDict( aLine );
4411  aLine.append( ">>>>\n" );
4412  }
4413  aLine.append( "/DA" );
4414  appendLiteralStringEncrypt( rWidget.m_aDAString, rWidget.m_nObject, aLine );
4415  aLine.append( "\n" );
4416  if( rWidget.m_nTextStyle & DrawTextFlags::Center )
4417  aLine.append( "/Q 1\n" );
4418  else if( rWidget.m_nTextStyle & DrawTextFlags::Right )
4419  aLine.append( "/Q 2\n" );
4420  }
4421  // appearance characteristics for terminal fields
4422  // which are supposed to have an appearance constructed
4423  // by the viewer application
4424  if( !rWidget.m_aMKDict.isEmpty() )
4425  {
4426  aLine.append( "/MK<<" );
4427  aLine.append( rWidget.m_aMKDict );
4428  //add the CA string, encrypting it
4429  appendLiteralStringEncrypt(rWidget.m_aMKDictCAString, rWidget.m_nObject, aLine);
4430  aLine.append( ">>\n" );
4431  }
4432 
4433  CHECK_RETURN( emitAppearances( rWidget, aLine ) );
4434 
4435  aLine.append( ">>\n"
4436  "endobj\n\n" );
4437  CHECK_RETURN( updateObject( rWidget.m_nObject ) );
4438  CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4439  }
4440  return true;
4441 }
4442 
4444 {
4445  if( m_aPages.empty() )
4446  return false;
4447 
4452 
4453  return true;
4454 }
4455 
4457 {
4458  for (auto& rEmbeddedFile : m_aEmbeddedFiles)
4459  {
4460  if (!updateObject(rEmbeddedFile.m_nObject))
4461  continue;
4462 
4463  OStringBuffer aLine;
4464  aLine.append(rEmbeddedFile.m_nObject);
4465  aLine.append(" 0 obj\n");
4466  aLine.append("<< /Type /EmbeddedFile /Length ");
4467  aLine.append(static_cast<sal_Int64>(rEmbeddedFile.m_pData->size()));
4468  aLine.append(" >>\nstream\n");
4469  CHECK_RETURN(writeBuffer(aLine.getStr(), aLine.getLength()));
4470  aLine.setLength(0);
4471 
4472  CHECK_RETURN(writeBuffer(rEmbeddedFile.m_pData->data(), rEmbeddedFile.m_pData->size()));
4473 
4474  aLine.append("\nendstream\nendobj\n\n");
4475  CHECK_RETURN(writeBuffer(aLine.getStr(), aLine.getLength()));
4476  }
4477  return true;
4478 }
4479 
4480 #undef CHECK_RETURN
4481 #define CHECK_RETURN( x ) if( !x ) return false
4482 
4484 {
4485  // build page tree
4486  // currently there is only one node that contains all leaves
4487 
4488  // first create a page tree node id
4489  sal_Int32 nTreeNode = createObject();
4490 
4491  // emit global resource dictionary (page emit needs it)
4493 
4494  // emit all pages
4495  for (auto & page : m_aPages)
4496  if( ! page.emit( nTreeNode ) )
4497  return false;
4498 
4499  sal_Int32 nNamedDestinationsDictionary = emitNamedDestinations();
4500 
4501  sal_Int32 nOutlineDict = emitOutline();
4502 
4503  // emit Output intent
4504  sal_Int32 nOutputIntentObject = emitOutputIntent();
4505 
4506  // emit metadata
4507  sal_Int32 nMetadataObject = emitDocumentMetadata();
4508 
4509  sal_Int32 nStructureDict = 0;
4510  if(m_aStructure.size() > 1)
4511  {
4512  // check if dummy structure containers are needed
4514  nStructureDict = m_aStructure[0].m_nObject = createObject();
4515  emitStructure( m_aStructure[ 0 ] );
4516  }
4517 
4518  // adjust tree node file offset
4519  if( ! updateObject( nTreeNode ) )
4520  return false;
4521 
4522  // emit tree node
4523  OStringBuffer aLine( 2048 );
4524  aLine.append( nTreeNode );
4525  aLine.append( " 0 obj\n" );
4526  aLine.append( "<</Type/Pages\n" );
4527  aLine.append( "/Resources " );
4528  aLine.append( getResourceDictObj() );
4529  aLine.append( " 0 R\n" );
4530 
4531  sal_Int32 nMediaBoxWidth = 0;
4532  sal_Int32 nMediaBoxHeight = 0;
4533  sal_Int32 nUserUnit = 1;
4534  if( m_aPages.empty() ) // sanity check, this should not happen
4535  {
4536  nMediaBoxWidth = g_nInheritedPageWidth;
4537  nMediaBoxHeight = g_nInheritedPageHeight;
4538  }
4539  else
4540  {
4541  for (auto const& page : m_aPages)
4542  {
4543  if( page.m_nPageWidth > nMediaBoxWidth )
4544  {
4545  nMediaBoxWidth = page.m_nPageWidth;
4546  nUserUnit = page.m_nUserUnit;
4547  }
4548  if( page.m_nPageHeight > nMediaBoxHeight )
4549  {
4550  nMediaBoxHeight = page.m_nPageHeight;
4551  nUserUnit = page.m_nUserUnit;
4552  }
4553  }
4554  }
4555  aLine.append( "/MediaBox[ 0 0 " );
4556  aLine.append(nMediaBoxWidth / nUserUnit);
4557  aLine.append( ' ' );
4558  aLine.append(nMediaBoxHeight / nUserUnit);
4559  aLine.append(" ]\n");
4560  if (nUserUnit > 1)
4561  {
4562  aLine.append("/UserUnit ");
4563  aLine.append(nUserUnit);
4564  aLine.append("\n");
4565  }
4566  aLine.append("/Kids[ ");
4567  unsigned int i = 0;
4568  for (const auto & page : m_aPages)
4569  {
4570  aLine.append( page.m_nPageObject );
4571  aLine.append( " 0 R" );
4572  aLine.append( ( (i&15) == 15 ) ? "\n" : " " );
4573  ++i;
4574  }
4575  aLine.append( "]\n"
4576  "/Count " );
4577  aLine.append( static_cast<sal_Int32>(m_aPages.size()) );
4578  aLine.append( ">>\n"
4579  "endobj\n\n" );
4580  CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4581 
4582  // emit annotation objects
4585 
4586  // emit Catalog
4588  if( ! updateObject( m_nCatalogObject ) )
4589  return false;
4590  aLine.setLength( 0 );
4591  aLine.append( m_nCatalogObject );
4592  aLine.append( " 0 obj\n"
4593  "<</Type/Catalog/Pages " );
4594  aLine.append( nTreeNode );
4595  aLine.append( " 0 R\n" );
4596 
4597  // check if there are named destinations to emit (root must be inside the catalog)
4598  if( nNamedDestinationsDictionary )
4599  {
4600  aLine.append("/Dests ");
4601  aLine.append( nNamedDestinationsDictionary );
4602  aLine.append( " 0 R\n" );
4603  }
4604 
4606  switch( m_aContext.PageLayout )
4607  {
4608  default :
4609  case PDFWriter::SinglePage :
4610  aLine.append( "/PageLayout/SinglePage\n" );
4611  break;
4612  case PDFWriter::Continuous :
4613  aLine.append( "/PageLayout/OneColumn\n" );
4614  break;
4616  // the flag m_aContext.FirstPageLeft below is used to set the page on the left side
4617  aLine.append( "/PageLayout/TwoColumnRight\n" );//odd page on the right side
4618  break;
4619  }
4621  switch( m_aContext.PDFDocumentMode )
4622  {
4623  default :
4624  aLine.append( "/PageMode/UseNone\n" );
4625  break;
4626  case PDFWriter::UseOutlines :
4627  aLine.append( "/PageMode/UseOutlines\n" ); //document is opened with outline pane open
4628  break;
4629  case PDFWriter::UseThumbs :
4630  aLine.append( "/PageMode/UseThumbs\n" ); //document is opened with thumbnails pane open
4631  break;
4632  }
4633  else if( m_aContext.OpenInFullScreenMode )
4634  aLine.append( "/PageMode/FullScreen\n" ); //document is opened full screen
4635 
4636  OStringBuffer aInitPageRef;
4637  if( m_aContext.InitialPage >= 0 && m_aContext.InitialPage < static_cast<sal_Int32>(m_aPages.size()) )
4638  {
4639  aInitPageRef.append( m_aPages[m_aContext.InitialPage].m_nPageObject );
4640  aInitPageRef.append( " 0 R" );
4641  }
4642  else
4643  aInitPageRef.append( "0" );
4644 
4645  switch( m_aContext.PDFDocumentAction )
4646  {
4647  case PDFWriter::ActionDefault : //do nothing, this is the Acrobat default
4648  default:
4649  if( aInitPageRef.getLength() > 1 )
4650  {
4651  aLine.append( "/OpenAction[" );
4652  aLine.append( aInitPageRef.makeStringAndClear() );
4653  aLine.append( " /XYZ null null 0]\n" );
4654  }
4655  break;
4656  case PDFWriter::FitInWindow :
4657  aLine.append( "/OpenAction[" );
4658  aLine.append( aInitPageRef.makeStringAndClear() );
4659  aLine.append( " /Fit]\n" ); //Open fit page
4660  break;
4661  case PDFWriter::FitWidth :
4662  aLine.append( "/OpenAction[" );
4663  aLine.append( aInitPageRef.makeStringAndClear() );
4664  aLine.append( " /FitH " );
4665  aLine.append( g_nInheritedPageHeight );//Open fit width
4666  aLine.append( "]\n" );
4667  break;
4668  case PDFWriter::FitVisible :
4669  aLine.append( "/OpenAction[" );
4670  aLine.append( aInitPageRef.makeStringAndClear() );
4671  aLine.append( " /FitBH " );
4672  aLine.append( g_nInheritedPageHeight );//Open fit visible
4673  aLine.append( "]\n" );
4674  break;
4675  case PDFWriter::ActionZoom :
4676  aLine.append( "/OpenAction[" );
4677  aLine.append( aInitPageRef.makeStringAndClear() );
4678  aLine.append( " /XYZ null null " );
4679  if( m_aContext.Zoom >= 50 && m_aContext.Zoom <= 1600 )
4680  aLine.append( static_cast<double>(m_aContext.Zoom)/100.0 );
4681  else
4682  aLine.append( "0" );
4683  aLine.append( "]\n" );
4684  break;
4685  }
4686 
4687  // viewer preferences, if we had some, then emit
4694  {
4695  aLine.append( "/ViewerPreferences<<" );
4697  aLine.append( "/HideToolbar true\n" );
4699  aLine.append( "/HideMenubar true\n" );
4701  aLine.append( "/HideWindowUI true\n" );
4702  if( m_aContext.FitWindow )
4703  aLine.append( "/FitWindow true\n" );
4704  if( m_aContext.CenterWindow )
4705  aLine.append( "/CenterWindow true\n" );
4707  aLine.append( "/DisplayDocTitle true\n" );
4709  aLine.append( "/Direction/R2L\n" );
4711  switch( m_aContext.PDFDocumentMode )
4712  {
4713  default :
4714  case PDFWriter::ModeDefault :
4715  aLine.append( "/NonFullScreenPageMode/UseNone\n" );
4716  break;
4717  case PDFWriter::UseOutlines :
4718  aLine.append( "/NonFullScreenPageMode/UseOutlines\n" );
4719  break;
4720  case PDFWriter::UseThumbs :
4721  aLine.append( "/NonFullScreenPageMode/UseThumbs\n" );
4722  break;
4723  }
4724  aLine.append( ">>\n" );
4725  }
4726 
4727  if( nOutlineDict )
4728  {
4729  aLine.append( "/Outlines " );
4730  aLine.append( nOutlineDict );
4731  aLine.append( " 0 R\n" );
4732  }
4733  if( nStructureDict )
4734  {
4735  aLine.append( "/StructTreeRoot " );
4736  aLine.append( nStructureDict );
4737  aLine.append( " 0 R\n" );
4738  }
4739  if( !m_aContext.DocumentLocale.Language.isEmpty() )
4740  {
4741  /* PDF allows only RFC 3066, see above in emitStructure(). */
4742  LanguageTag aLanguageTag( m_aContext.DocumentLocale);
4743  OUString aLanguage, aScript, aCountry;
4744  aLanguageTag.getIsoLanguageScriptCountry( aLanguage, aScript, aCountry);
4745  if (!aLanguage.isEmpty())
4746  {
4747  OUStringBuffer aLocBuf( 16 );
4748  aLocBuf.append( aLanguage );
4749  if( !aCountry.isEmpty() )
4750  {
4751  aLocBuf.append( '-' );
4752  aLocBuf.append( aCountry );
4753  }
4754  aLine.append( "/Lang" );
4755  appendLiteralStringEncrypt( aLocBuf.makeStringAndClear(), m_nCatalogObject, aLine );
4756  aLine.append( "\n" );
4757  }
4758  }
4760  {
4761  aLine.append( "/MarkInfo<</Marked true>>\n" );
4762  }
4763  if( !m_aWidgets.empty() )
4764  {
4765  aLine.append( "/AcroForm<</Fields[\n" );
4766  int nWidgets = m_aWidgets.size();
4767  int nOut = 0;
4768  for( int j = 0; j < nWidgets; j++ )
4769  {
4770  // output only root fields
4771  if( m_aWidgets[j].m_nParent < 1 )
4772  {
4773  aLine.append( m_aWidgets[j].m_nObject );
4774  aLine.append( (nOut++ % 5)==4 ? " 0 R\n" : " 0 R " );
4775  }
4776  }
4777  aLine.append( "\n]" );
4778 
4779 #if HAVE_FEATURE_NSS
4780  if (m_nSignatureObject != -1)
4781  aLine.append( "/SigFlags 3");
4782 #endif
4783 
4784  aLine.append( "/DR " );
4785  aLine.append( getResourceDictObj() );
4786  aLine.append( " 0 R" );
4787  // NeedAppearances must not be used if PDF is signed
4789 #if HAVE_FEATURE_NSS
4790  || ( m_nSignatureObject != -1 )
4791 #endif
4792  )
4793  aLine.append( ">>\n" );
4794  else
4795  aLine.append( "/NeedAppearances true>>\n" );
4796  }
4797 
4798  //check if there is a Metadata object
4799  if( nOutputIntentObject )
4800  {
4801  aLine.append("/OutputIntents[");
4802  aLine.append( nOutputIntentObject );
4803  aLine.append( " 0 R]" );
4804  }
4805 
4806  if( nMetadataObject )
4807  {
4808  aLine.append("/Metadata ");
4809  aLine.append( nMetadataObject );
4810  aLine.append( " 0 R" );
4811  }
4812 
4813  aLine.append( ">>\n"
4814  "endobj\n\n" );
4815  return writeBuffer( aLine.getStr(), aLine.getLength() );
4816 }
4817 
4818 #if HAVE_FEATURE_NSS
4819 
4821 {
4823  return false;
4824 
4825  OStringBuffer aLine( 0x5000 );
4826  aLine.append( m_nSignatureObject );
4827  aLine.append( " 0 obj\n" );
4828  aLine.append("<</Contents <" );
4829 
4830  sal_uInt64 nOffset = ~0U;
4831  CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nOffset) ) );
4832 
4833  m_nSignatureContentOffset = nOffset + aLine.getLength();
4834 
4835  // reserve some space for the PKCS#7 object
4836  OStringBuffer aContentFiller( MAX_SIGNATURE_CONTENT_LENGTH );
4838  aLine.append( aContentFiller.makeStringAndClear() );
4839  aLine.append( ">\n/Type/Sig/SubFilter/adbe.pkcs7.detached");
4840 
4841  if( !m_aContext.DocumentInfo.Author.isEmpty() )
4842  {
4843  aLine.append( "/Name" );
4845  }
4846 
4847  aLine.append( " /M ");
4849 
4850  aLine.append( " /ByteRange [ 0 ");
4851  aLine.append( m_nSignatureContentOffset - 1 );
4852  aLine.append( " " );
4854  aLine.append( " " );
4855 
4856  m_nSignatureLastByteRangeNoOffset = nOffset + aLine.getLength();
4857 
4858  // mark the last ByteRange no and add some space. Now, we don't know
4859  // how many bytes we need for this ByteRange value
4860  // The real value will be overwritten in the finalizeSignature method
4861  OStringBuffer aByteRangeFiller( 100 );
4862  comphelper::string::padToLength(aByteRangeFiller, 100, ' ');
4863  aLine.append( aByteRangeFiller.makeStringAndClear() );
4864  aLine.append(" /Filter/Adobe.PPKMS");
4865 
4866  //emit reason, location and contactinfo
4867  if ( !m_aContext.SignReason.isEmpty() )
4868  {
4869  aLine.append("/Reason");
4871  }
4872 
4873  if ( !m_aContext.SignLocation.isEmpty() )
4874  {
4875  aLine.append("/Location");
4877  }
4878 
4879  if ( !m_aContext.SignContact.isEmpty() )
4880  {
4881  aLine.append("/ContactInfo");
4883  }
4884 
4885  aLine.append(" >>\nendobj\n\n" );
4886 
4887  return writeBuffer( aLine.getStr(), aLine.getLength() );
4888 }
4889 
4891 {
4892  if (!m_aContext.SignCertificate.is())
4893  return false;
4894 
4895  // 1- calculate last ByteRange value
4896  sal_uInt64 nOffset = ~0U;
4897  CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nOffset) ) );
4898 
4899  sal_Int64 nLastByteRangeNo = nOffset - (m_nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1);
4900 
4901  // 2- overwrite the value to the m_nSignatureLastByteRangeNoOffset position
4902  sal_uInt64 nWritten = 0;
4903  CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, m_nSignatureLastByteRangeNoOffset) ) );
4904  OString aByteRangeNo = OString::number( nLastByteRangeNo ) + " ]";
4905 
4906  if (m_aFile.write(aByteRangeNo.getStr(), aByteRangeNo.getLength(), nWritten) != osl::File::E_None)
4907  {
4908  CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, nOffset)) );
4909  return false;
4910  }
4911 
4912  // 3- create the PKCS#7 object using NSS
4913 
4914  // Prepare buffer and calculate PDF file digest
4915  CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, 0)) );
4916 
4917  std::unique_ptr<char[]> buffer1(new char[m_nSignatureContentOffset + 1]);
4918  sal_uInt64 bytesRead1;
4919 
4920  //FIXME: Check if hash is calculated from the correct byterange
4921  if (osl::File::E_None != m_aFile.read(buffer1.get(), m_nSignatureContentOffset - 1 , bytesRead1) ||
4922  bytesRead1 != static_cast<sal_uInt64>(m_nSignatureContentOffset) - 1)
4923  {
4924  SAL_WARN("vcl.pdfwriter", "First buffer read failed");
4925  return false;
4926  }
4927 
4928  std::unique_ptr<char[]> buffer2(new char[nLastByteRangeNo + 1]);
4929  sal_uInt64 bytesRead2;
4930 
4931  if (osl::File::E_None != m_aFile.setPos(osl_Pos_Absolut, m_nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1) ||
4932  osl::File::E_None != m_aFile.read(buffer2.get(), nLastByteRangeNo, bytesRead2) ||
4933  bytesRead2 != static_cast<sal_uInt64>(nLastByteRangeNo))
4934  {
4935  SAL_WARN("vcl.pdfwriter", "Second buffer read failed");
4936  return false;
4937  }
4938 
4939  OStringBuffer aCMSHexBuffer;
4941  aSigning.AddDataRange(buffer1.get(), bytesRead1);
4942  aSigning.AddDataRange(buffer2.get(), bytesRead2);
4943  aSigning.SetSignTSA(m_aContext.SignTSA);
4944  aSigning.SetSignPassword(m_aContext.SignPassword);
4945  if (!aSigning.Sign(aCMSHexBuffer))
4946  {
4947  SAL_WARN("vcl.pdfwriter", "PDFWriter::Sign() failed");
4948  return false;
4949  }
4950 
4951  assert(aCMSHexBuffer.getLength() <= MAX_SIGNATURE_CONTENT_LENGTH);
4952 
4953  // Set file pointer to the m_nSignatureContentOffset, we're ready to overwrite PKCS7 object
4954  nWritten = 0;
4955  CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, m_nSignatureContentOffset)) );
4956  m_aFile.write(aCMSHexBuffer.getStr(), aCMSHexBuffer.getLength(), nWritten);
4957 
4958  return osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, nOffset);
4959 }
4960 
4961 #endif //HAVE_FEATURE_NSS
4962 
4964 {
4965  sal_Int32 nObject = createObject();
4966 
4967  if( updateObject( nObject ) )
4968  {
4969  OStringBuffer aLine( 1024 );
4970  aLine.append( nObject );
4971  aLine.append( " 0 obj\n"
4972  "<<" );
4973  if( !m_aContext.DocumentInfo.Title.isEmpty() )
4974  {
4975  aLine.append( "/Title" );
4977  aLine.append( "\n" );
4978  }
4979  if( !m_aContext.DocumentInfo.Author.isEmpty() )
4980  {
4981  aLine.append( "/Author" );
4983  aLine.append( "\n" );
4984  }
4985  if( !m_aContext.DocumentInfo.Subject.isEmpty() )
4986  {
4987  aLine.append( "/Subject" );
4989  aLine.append( "\n" );
4990  }
4991  if( !m_aContext.DocumentInfo.Keywords.isEmpty() )
4992  {
4993  aLine.append( "/Keywords" );
4995  aLine.append( "\n" );
4996  }
4997  if( !m_aContext.DocumentInfo.Creator.isEmpty() )
4998  {
4999  aLine.append( "/Creator" );
5001  aLine.append( "\n" );
5002  }
5003  if( !m_aContext.DocumentInfo.Producer.isEmpty() )
5004  {
5005  aLine.append( "/Producer" );
5007  aLine.append( "\n" );
5008  }
5009 
5010  aLine.append( "/CreationDate" );
5012  aLine.append( ">>\nendobj\n\n" );
5013  if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
5014  nObject = 0;
5015  }
5016  else
5017  nObject = 0;
5018 
5019  return nObject;
5020 }
5021 
5022 // Part of this function may be shared with method appendDest.
5024 {
5025  sal_Int32 nCount = m_aNamedDests.size();
5026  if( nCount <= 0 )
5027  return 0;//define internal error
5028 
5029  //get the object number for all the destinations
5030  sal_Int32 nObject = createObject();
5031 
5032  if( updateObject( nObject ) )
5033  {
5034  //emit the dictionary
5035  OStringBuffer aLine( 1024 );
5036  aLine.append( nObject );
5037  aLine.append( " 0 obj\n"
5038  "<<" );
5039 
5040  sal_Int32 nDestID;
5041  for( nDestID = 0; nDestID < nCount; nDestID++ )
5042  {
5043  const PDFNamedDest& rDest = m_aNamedDests[ nDestID ];
5044  // In order to correctly function both under an Internet browser and
5045  // directly with a reader (provided the reader has the feature) we
5046  // need to set the name of the destination the same way it will be encoded
5047  // in an Internet link
5048  INetURLObject aLocalURL( "http://ahost.ax" ); //dummy location, won't be used
5049  aLocalURL.SetMark( rDest.m_aDestName );
5050 
5051  const OUString aName = aLocalURL.GetMark( INetURLObject::DecodeMechanism::NONE ); //same coding as
5052  // in link creation ( see PDFWriterImpl::emitLinkAnnotations )
5053  const PDFPage& rDestPage = m_aPages[ rDest.m_nPage ];
5054 
5055  aLine.append( '/' );
5056  appendDestinationName( aName, aLine ); // this conversion must be done when forming the link to target ( see in emitCatalog )