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