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