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