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