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