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