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