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