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