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