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