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 }
5038
5039 CHECK_RETURN( emitAppearances( rWidget, aLine ) );
5040
5041 aLine.append( ">>\n"
5042 "endobj\n\n" );
5043 CHECK_RETURN( updateObject( rWidget.m_nObject ) );
5044 CHECK_RETURN( writeBuffer( aLine ) );
5045 }
5046 return true;
5047}
5048
5050{
5051 if( m_aPages.empty() )
5052 return false;
5053
5058
5059 return true;
5060}
5061
5062class PDFStreamIf : public cppu::WeakImplHelper< css::io::XOutputStream >
5063{
5066 public:
5067 explicit PDFStreamIf( PDFWriterImpl* pWriter ) : m_pWriter( pWriter ), m_bWrite( true ) {}
5068
5069 virtual void SAL_CALL writeBytes( const css::uno::Sequence< sal_Int8 >& aData ) override
5070 {
5071 if( m_bWrite && aData.hasElements() )
5072 {
5073 sal_Int32 nBytes = aData.getLength();
5074 m_pWriter->writeBufferBytes( aData.getConstArray(), nBytes );
5075 }
5076 }
5077 virtual void SAL_CALL flush() override {}
5078 virtual void SAL_CALL closeOutput() override
5079 {
5080 m_bWrite = false;
5081 }
5082};
5083
5085{
5086 for (auto& rEmbeddedFile : m_aEmbeddedFiles)
5087 {
5088 if (!updateObject(rEmbeddedFile.m_nObject))
5089 continue;
5090
5091 sal_Int32 nSizeObject = createObject();
5092 sal_Int32 nParamsObject = createObject();
5093
5094 OStringBuffer aLine;
5095 aLine.append(rEmbeddedFile.m_nObject);
5096 aLine.append(" 0 obj\n");
5097 aLine.append("<< /Type /EmbeddedFile");
5098 if (!rEmbeddedFile.m_aSubType.isEmpty())
5099 {
5100 aLine.append("/Subtype /");
5101 appendName(rEmbeddedFile.m_aSubType, aLine);
5102 }
5103 aLine.append(" /Length ");
5104 appendObjectReference(nSizeObject, aLine);
5105 aLine.append(" /Params ");
5106 appendObjectReference(nParamsObject, aLine);
5107 aLine.append(">>\nstream\n");
5108 checkAndEnableStreamEncryption(rEmbeddedFile.m_nObject);
5109 CHECK_RETURN(writeBuffer(aLine));
5111 aLine.setLength(0);
5112
5113 sal_Int64 nSize{};
5114 if (!rEmbeddedFile.m_aDataContainer.isEmpty())
5115 {
5116 nSize = rEmbeddedFile.m_aDataContainer.getSize();
5117 CHECK_RETURN(writeBufferBytes(rEmbeddedFile.m_aDataContainer.getData(), rEmbeddedFile.m_aDataContainer.getSize()));
5118 }
5119 else if (rEmbeddedFile.m_pStream)
5120 {
5121 sal_uInt64 nBegin = getCurrentFilePosition();
5122 css::uno::Reference<css::io::XOutputStream> xStream(new PDFStreamIf(this));
5123 rEmbeddedFile.m_pStream->write(xStream);
5124 rEmbeddedFile.m_pStream.reset();
5125 xStream.clear();
5126 nSize = sal_Int64(getCurrentFilePosition() - nBegin);
5127 }
5128 aLine.append("\nendstream\nendobj\n\n");
5129 CHECK_RETURN(writeBuffer(aLine));
5130 aLine.setLength(0);
5131
5132 if (!updateObject(nSizeObject))
5133 return false;
5134 aLine.append(nSizeObject);
5135 aLine.append(" 0 obj\n");
5136 aLine.append(nSize);
5137 aLine.append("\nendobj\n\n");
5138 if (!writeBuffer(aLine))
5139 return false;
5140 aLine.setLength(0);
5141
5142 if (!updateObject(nParamsObject))
5143 return false;
5144 aLine.append(nParamsObject);
5145 aLine.append(" 0 obj\n");
5146 aLine.append("<<");
5147 aLine.append("/Size ");
5148 aLine.append(nSize);
5149 aLine.append(">>");
5150 aLine.append("\nendobj\n\n");
5151 if (!writeBuffer(aLine))
5152 return false;
5153 }
5154 return true;
5155}
5156
5157#undef CHECK_RETURN
5158#define CHECK_RETURN( x ) if( !x ) return false
5159
5161{
5162 // build page tree
5163 // currently there is only one node that contains all leaves
5164
5165 // first create a page tree node id
5166 sal_Int32 nTreeNode = createObject();
5167
5168 // emit global resource dictionary (page emit needs it)
5170
5171 // emit all pages
5172 for (auto & page : m_aPages)
5173 if( ! page.emit( nTreeNode ) )
5174 return false;
5175
5176 sal_Int32 nNamedDestinationsDictionary = emitNamedDestinations();
5177
5178 sal_Int32 nOutlineDict = emitOutline();
5179
5180 // emit Output intent
5181 sal_Int32 nOutputIntentObject = emitOutputIntent();
5182
5183 // emit metadata
5184 sal_Int32 nMetadataObject = emitDocumentMetadata();
5185
5186 sal_Int32 nStructureDict = 0;
5187 if(m_aStructure.size() > 1)
5188 {
5189 removePlaceholderSE(m_aStructure, m_aStructure[0]);
5190 // check if dummy structure containers are needed
5192 nStructureDict = m_aStructure[0].m_nObject = createObject();
5194 }
5195
5196 // adjust tree node file offset
5197 if( ! updateObject( nTreeNode ) )
5198 return false;
5199
5200 // emit tree node
5201 OStringBuffer aLine( 2048 );
5202 aLine.append( nTreeNode );
5203 aLine.append( " 0 obj\n" );
5204 aLine.append( "<</Type/Pages\n" );
5205 aLine.append( "/Resources " );
5206 aLine.append( getResourceDictObj() );
5207 aLine.append( " 0 R\n" );
5208
5209 double nMediaBoxWidth = 0;
5210 double nMediaBoxHeight = 0;
5211 sal_Int32 nUserUnit = 1;
5212 if( m_aPages.empty() ) // sanity check, this should not happen
5213 {
5214 nMediaBoxWidth = g_nInheritedPageWidth;
5215 nMediaBoxHeight = g_nInheritedPageHeight;
5216 }
5217 else
5218 {
5219 for (auto const& page : m_aPages)
5220 {
5221 if( page.m_nPageWidth > nMediaBoxWidth )
5222 {
5223 nMediaBoxWidth = page.m_nPageWidth;
5224 nUserUnit = page.m_nUserUnit;
5225 }
5226 if( page.m_nPageHeight > nMediaBoxHeight )
5227 {
5228 nMediaBoxHeight = page.m_nPageHeight;
5229 nUserUnit = page.m_nUserUnit;
5230 }
5231 }
5232 }
5233 aLine.append( "/MediaBox[ 0 0 " );
5234 aLine.append(nMediaBoxWidth / nUserUnit);
5235 aLine.append( ' ' );
5236 aLine.append(nMediaBoxHeight / nUserUnit);
5237 aLine.append(" ]\n");
5238 if (nUserUnit > 1)
5239 {
5240 aLine.append("/UserUnit ");
5241 aLine.append(nUserUnit);
5242 aLine.append("\n");
5243 }
5244 aLine.append("/Kids[ ");
5245 unsigned int i = 0;
5246 for (const auto & page : m_aPages)
5247 {
5248 aLine.append( page.m_nPageObject );
5249 aLine.append( " 0 R" );
5250 aLine.append( ( (i&15) == 15 ) ? "\n" : " " );
5251 ++i;
5252 }
5253 aLine.append( "]\n"
5254 "/Count " );
5255 aLine.append( static_cast<sal_Int32>(m_aPages.size()) );
5256 aLine.append( ">>\n"
5257 "endobj\n\n" );
5258 CHECK_RETURN( writeBuffer( aLine ) );
5259
5260 // emit annotation objects
5263
5264 // emit attached files
5265 for (auto & rAttachedFile : m_aDocumentAttachedFiles)
5266 {
5267 if (!updateObject(rAttachedFile.mnObjectId))
5268 return false;
5269 aLine.setLength( 0 );
5270
5271 appendObjectID(rAttachedFile.mnObjectId, aLine);
5272 aLine.append("<</Type /Filespec");
5273 aLine.append("/F<");
5274 PDFWriter::AppendUnicodeTextString(rAttachedFile.maFilename, aLine);
5275 aLine.append("> ");
5277 {
5278 aLine.append("/UF<");
5279 PDFWriter::AppendUnicodeTextString(rAttachedFile.maFilename, aLine);
5280 aLine.append("> ");
5281 }
5282 if (!rAttachedFile.maDescription.isEmpty())
5283 {
5284 aLine.append("/Desc <");
5285 PDFWriter::AppendUnicodeTextString(rAttachedFile.maDescription, aLine);
5286 aLine.append("> ");
5287 }
5288 aLine.append("/EF <</F ");
5289 appendObjectReference(rAttachedFile.mnEmbeddedFileObjectId, aLine);
5290 aLine.append(">>");
5291 aLine.append(">>\nendobj\n\n");
5292 CHECK_RETURN( writeBuffer( aLine ) );
5293 }
5294
5295 // emit Catalog
5298 return false;
5299 aLine.setLength( 0 );
5300 aLine.append( m_nCatalogObject );
5301 aLine.append( " 0 obj\n"
5302 "<</Type/Catalog/Pages " );
5303 aLine.append( nTreeNode );
5304 aLine.append( " 0 R\n" );
5305
5306 // check if there are named destinations to emit (root must be inside the catalog)
5307 if( nNamedDestinationsDictionary )
5308 {
5309 aLine.append("/Dests ");
5310 aLine.append( nNamedDestinationsDictionary );
5311 aLine.append( " 0 R\n" );
5312 }
5313
5314 if (!m_aDocumentAttachedFiles.empty())
5315 {
5316 aLine.append("/Names ");
5317 aLine.append("<</EmbeddedFiles <</Names [");
5318 for (auto & rAttachedFile : m_aDocumentAttachedFiles)
5319 {
5320 aLine.append('<');
5321 PDFWriter::AppendUnicodeTextString(rAttachedFile.maFilename, aLine);
5322 aLine.append('>');
5323 aLine.append(' ');
5324 appendObjectReference(rAttachedFile.mnObjectId, aLine);
5325 }
5326 aLine.append("]>>>>");
5327 aLine.append("\n" );
5328 }
5329
5331 switch( m_aContext.PageLayout )
5332 {
5333 default :
5335 aLine.append( "/PageLayout/SinglePage\n" );
5336 break;
5338 aLine.append( "/PageLayout/OneColumn\n" );
5339 break;
5341 // the flag m_aContext.FirstPageLeft below is used to set the page on the left side
5342 aLine.append( "/PageLayout/TwoColumnRight\n" );//odd page on the right side
5343 break;
5344 }
5346 switch( m_aContext.PDFDocumentMode )
5347 {
5348 default :
5349 aLine.append( "/PageMode/UseNone\n" );
5350 break;
5352 aLine.append( "/PageMode/UseOutlines\n" ); //document is opened with outline pane open
5353 break;
5355 aLine.append( "/PageMode/UseThumbs\n" ); //document is opened with thumbnails pane open
5356 break;
5357 }
5359 aLine.append( "/PageMode/FullScreen\n" ); //document is opened full screen
5360
5361 OStringBuffer aInitPageRef;
5363 {
5364 aInitPageRef.append( m_aPages[m_aContext.InitialPage].m_nPageObject );
5365 aInitPageRef.append( " 0 R" );
5366 }
5367 else
5368 aInitPageRef.append( "0" );
5369
5371 {
5372 case PDFWriter::ActionDefault : //do nothing, this is the Acrobat default
5373 default:
5374 if( aInitPageRef.getLength() > 1 )
5375 {
5376 aLine.append( "/OpenAction[" );
5377 aLine.append( aInitPageRef );
5378 aLine.append( " /XYZ null null 0]\n" );
5379 }
5380 break;
5382 aLine.append( "/OpenAction[" );
5383 aLine.append( aInitPageRef );
5384 aLine.append( " /Fit]\n" ); //Open fit page
5385 break;
5386 case PDFWriter::FitWidth :
5387 aLine.append( "/OpenAction[" );
5388 aLine.append( aInitPageRef );
5389 aLine.append( " /FitH " );
5390 aLine.append( g_nInheritedPageHeight );//Open fit width
5391 aLine.append( "]\n" );
5392 break;
5394 aLine.append( "/OpenAction[" );
5395 aLine.append( aInitPageRef );
5396 aLine.append( " /FitBH " );
5397 aLine.append( g_nInheritedPageHeight );//Open fit visible
5398 aLine.append( "]\n" );
5399 break;
5401 aLine.append( "/OpenAction[" );
5402 aLine.append( aInitPageRef );
5403 aLine.append( " /XYZ null null " );
5404 if( m_aContext.Zoom >= 50 && m_aContext.Zoom <= 1600 )
5405 aLine.append( static_cast<double>(m_aContext.Zoom)/100.0 );
5406 else
5407 aLine.append( "0" );
5408 aLine.append( "]\n" );
5409 break;
5410 }
5411
5412 // viewer preferences, if we had some, then emit
5419 {
5420 aLine.append( "/ViewerPreferences<<" );
5422 aLine.append( "/HideToolbar true\n" );
5424 aLine.append( "/HideMenubar true\n" );
5426 aLine.append( "/HideWindowUI true\n" );
5427 if( m_aContext.FitWindow )
5428 aLine.append( "/FitWindow true\n" );
5430 aLine.append( "/CenterWindow true\n" );
5432 aLine.append( "/DisplayDocTitle true\n" );
5434 aLine.append( "/Direction/R2L\n" );
5436 switch( m_aContext.PDFDocumentMode )
5437 {
5438 default :
5440 aLine.append( "/NonFullScreenPageMode/UseNone\n" );
5441 break;
5443 aLine.append( "/NonFullScreenPageMode/UseOutlines\n" );
5444 break;
5446 aLine.append( "/NonFullScreenPageMode/UseThumbs\n" );
5447 break;
5448 }
5449 aLine.append( ">>\n" );
5450 }
5451
5452 if( nOutlineDict )
5453 {
5454 aLine.append( "/Outlines " );
5455 aLine.append( nOutlineDict );
5456 aLine.append( " 0 R\n" );
5457 }
5458 if( nStructureDict )
5459 {
5460 aLine.append( "/StructTreeRoot " );
5461 aLine.append( nStructureDict );
5462 aLine.append( " 0 R\n" );
5463 }
5464 if( !m_aContext.DocumentLocale.Language.isEmpty() )
5465 {
5466 /* PDF allows only RFC 3066, see above in emitStructure(). */
5467 LanguageTag aLanguageTag( m_aContext.DocumentLocale);
5468 OUString aLanguage, aScript, aCountry;
5469 aLanguageTag.getIsoLanguageScriptCountry( aLanguage, aScript, aCountry);
5470 if (!aLanguage.isEmpty())
5471 {
5472 OUStringBuffer aLocBuf( 16 );
5473 aLocBuf.append( aLanguage );
5474 if( !aCountry.isEmpty() )
5475 {
5476 aLocBuf.append( '-' );
5477 aLocBuf.append( aCountry );
5478 }
5479 aLine.append( "/Lang" );
5481 aLine.append( "\n" );
5482 }
5483 }
5484 if (m_aContext.Tagged)
5485 {
5486 aLine.append( "/MarkInfo<</Marked true>>\n" );
5487 }
5488 if( !m_aWidgets.empty() )
5489 {
5490 aLine.append( "/AcroForm<</Fields[\n" );
5491 int nWidgets = m_aWidgets.size();
5492 int nOut = 0;
5493 for( int j = 0; j < nWidgets; j++ )
5494 {
5495 // output only root fields
5496 if( m_aWidgets[j].m_nParent < 1 )
5497 {
5498 aLine.append( m_aWidgets[j].m_nObject );
5499 aLine.append( (nOut++ % 5)==4 ? " 0 R\n" : " 0 R " );
5500 }
5501 }
5502 aLine.append( "\n]" );
5503
5504#if HAVE_FEATURE_NSS
5505 if (m_nSignatureObject != -1)
5506 aLine.append( "/SigFlags 3");
5507#endif
5508
5509 aLine.append( "/DR " );
5510 aLine.append( getResourceDictObj() );
5511 aLine.append( " 0 R" );
5512 // NeedAppearances must not be used if PDF is signed
5514#if HAVE_FEATURE_NSS
5515 || ( m_nSignatureObject != -1 )
5516#endif
5517 )
5518 aLine.append( ">>\n" );
5519 else
5520 aLine.append( "/NeedAppearances true>>\n" );
5521 }
5522
5523 //check if there is a Metadata object
5524 if( nOutputIntentObject )
5525 {
5526 aLine.append("/OutputIntents[");
5527 aLine.append( nOutputIntentObject );
5528 aLine.append( " 0 R]" );
5529 }
5530
5531 if( nMetadataObject )
5532 {
5533 aLine.append("/Metadata ");
5534 aLine.append( nMetadataObject );
5535 aLine.append( " 0 R" );
5536 }
5537
5538 aLine.append( ">>\n"
5539 "endobj\n\n" );
5540 return writeBuffer( aLine );
5541}
5542
5543#if HAVE_FEATURE_NSS
5544
5546{
5548 return false;
5549
5550 OStringBuffer aLine( 0x5000 );
5551 aLine.append( m_nSignatureObject );
5552 aLine.append( " 0 obj\n" );
5553 aLine.append("<</Contents <" );
5554
5555 sal_uInt64 nOffset = ~0U;
5556 CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nOffset) ) );
5557
5558 m_nSignatureContentOffset = nOffset + aLine.getLength();
5559
5560 // reserve some space for the PKCS#7 object
5561 OStringBuffer aContentFiller( MAX_SIGNATURE_CONTENT_LENGTH );
5562 comphelper::string::padToLength(aContentFiller, MAX_SIGNATURE_CONTENT_LENGTH, '0');
5563 aLine.append( aContentFiller );
5564 aLine.append( ">\n/Type/Sig/SubFilter/adbe.pkcs7.detached");
5565
5566 if( !m_aContext.DocumentInfo.Author.isEmpty() )
5567 {
5568 aLine.append( "/Name" );
5570 }
5571
5572 aLine.append( " /M ");
5574
5575 aLine.append( " /ByteRange [ 0 ");
5576 aLine.append( m_nSignatureContentOffset - 1 );
5577 aLine.append( " " );
5578 aLine.append( m_nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1 );
5579 aLine.append( " " );
5580
5581 m_nSignatureLastByteRangeNoOffset = nOffset + aLine.getLength();
5582
5583 // mark the last ByteRange no and add some space. Now, we don't know
5584 // how many bytes we need for this ByteRange value
5585 // The real value will be overwritten in the finalizeSignature method
5586 OStringBuffer aByteRangeFiller( 100 );
5587 comphelper::string::padToLength(aByteRangeFiller, 100, ' ');
5588 aLine.append( aByteRangeFiller );
5589 aLine.append(" /Filter/Adobe.PPKMS");
5590
5591 //emit reason, location and contactinfo
5592 if ( !m_aContext.SignReason.isEmpty() )
5593 {
5594 aLine.append("/Reason");
5596 }
5597
5598 if ( !m_aContext.SignLocation.isEmpty() )
5599 {
5600 aLine.append("/Location");
5602 }
5603
5604 if ( !m_aContext.SignContact.isEmpty() )
5605 {
5606 aLine.append("/ContactInfo");
5608 }
5609
5610 aLine.append(" >>\nendobj\n\n" );
5611
5612 return writeBuffer( aLine );
5613}
5614
5616{
5617 if (!m_aContext.SignCertificate.is())
5618 return false;
5619
5620 // 1- calculate last ByteRange value
5621 sal_uInt64 nOffset = ~0U;
5622 CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nOffset) ) );
5623
5624 sal_Int64 nLastByteRangeNo = nOffset - (m_nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1);
5625
5626 // 2- overwrite the value to the m_nSignatureLastByteRangeNoOffset position
5627 sal_uInt64 nWritten = 0;
5628 CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, m_nSignatureLastByteRangeNoOffset) ) );
5629 OString aByteRangeNo = OString::number( nLastByteRangeNo ) + " ]";
5630
5631 if (m_aFile.write(aByteRangeNo.getStr(), aByteRangeNo.getLength(), nWritten) != osl::File::E_None)
5632 {
5633 CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, nOffset)) );
5634 return false;
5635 }
5636
5637 // 3- create the PKCS#7 object using NSS
5638
5639 // Prepare buffer and calculate PDF file digest
5640 CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, 0)) );
5641
5642 std::unique_ptr<char[]> buffer1(new char[m_nSignatureContentOffset + 1]);
5643 sal_uInt64 bytesRead1;
5644
5645 //FIXME: Check if hash is calculated from the correct byterange
5646 if (osl::File::E_None != m_aFile.read(buffer1.get(), m_nSignatureContentOffset - 1 , bytesRead1) ||
5647 bytesRead1 != static_cast<sal_uInt64>(m_nSignatureContentOffset) - 1)
5648 {
5649 SAL_WARN("vcl.pdfwriter", "First buffer read failed");
5650 return false;
5651 }
5652
5653 std::unique_ptr<char[]> buffer2(new char[nLastByteRangeNo + 1]);
5654 sal_uInt64 bytesRead2;
5655
5656 if (osl::File::E_None != m_aFile.setPos(osl_Pos_Absolut, m_nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1) ||
5657 osl::File::E_None != m_aFile.read(buffer2.get(), nLastByteRangeNo, bytesRead2) ||
5658 bytesRead2 != static_cast<sal_uInt64>(nLastByteRangeNo))
5659 {
5660 SAL_WARN("vcl.pdfwriter", "Second buffer read failed");
5661 return false;
5662 }
5663
5664 OStringBuffer aCMSHexBuffer;
5666 aSigning.AddDataRange(buffer1.get(), bytesRead1);
5667 aSigning.AddDataRange(buffer2.get(), bytesRead2);
5668 aSigning.SetSignTSA(m_aContext.SignTSA);
5669 aSigning.SetSignPassword(m_aContext.SignPassword);
5670 if (!aSigning.Sign(aCMSHexBuffer))
5671 {
5672 SAL_WARN("vcl.pdfwriter", "PDFWriter::Sign() failed");
5673 return false;
5674 }
5675
5676 assert(aCMSHexBuffer.getLength() <= MAX_SIGNATURE_CONTENT_LENGTH);
5677
5678 // Set file pointer to the m_nSignatureContentOffset, we're ready to overwrite PKCS7 object
5679 nWritten = 0;
5680 CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, m_nSignatureContentOffset)) );
5681 m_aFile.write(aCMSHexBuffer.getStr(), aCMSHexBuffer.getLength(), nWritten);
5682
5683 return osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, nOffset);
5684}
5685
5686#endif //HAVE_FEATURE_NSS
5687
5689{
5690 sal_Int32 nObject = createObject();
5691
5692 if( updateObject( nObject ) )
5693 {
5694 OStringBuffer aLine( 1024 );
5695 aLine.append( nObject );
5696 aLine.append( " 0 obj\n"
5697 "<<" );
5698 if( !m_aContext.DocumentInfo.Title.isEmpty() )
5699 {
5700 aLine.append( "/Title" );
5702 aLine.append( "\n" );
5703 }
5704 if( !m_aContext.DocumentInfo.Author.isEmpty() )
5705 {
5706 aLine.append( "/Author" );
5708 aLine.append( "\n" );
5709 }
5710 if( !m_aContext.DocumentInfo.Subject.isEmpty() )
5711 {
5712 aLine.append( "/Subject" );
5714 aLine.append( "\n" );
5715 }
5716 if( !m_aContext.DocumentInfo.Keywords.isEmpty() )
5717 {
5718 aLine.append( "/Keywords" );
5720 aLine.append( "\n" );
5721 }
5722 if( !m_aContext.DocumentInfo.Creator.isEmpty() )
5723 {
5724 aLine.append( "/Creator" );
5726 aLine.append( "\n" );
5727 }
5728 if( !m_aContext.DocumentInfo.Producer.isEmpty() )
5729 {
5730 aLine.append( "/Producer" );
5732 aLine.append( "\n" );
5733 }
5734
5735 aLine.append( "/CreationDate" );
5737 aLine.append( ">>\nendobj\n\n" );
5738 if( ! writeBuffer( aLine ) )
5739 nObject = 0;
5740 }
5741 else
5742 nObject = 0;
5743
5744 return nObject;
5745}
5746
5747// Part of this function may be shared with method appendDest.
5749{
5750 sal_Int32 nCount = m_aNamedDests.size();
5751 if( nCount <= 0 )
5752 return 0;//define internal error
5753
5754 //get the object number for all the destinations
5755 sal_Int32 nObject = createObject();
5756
5757 if( updateObject( nObject ) )
5758 {
5759 //emit the dictionary
5760 OStringBuffer aLine( 1024 );
5761 aLine.append( nObject );
5762 aLine.append( " 0 obj\n"
5763 "<<" );
5764
5765 sal_Int32 nDestID;
5766 for( nDestID = 0; nDestID < nCount; nDestID++ )
5767 {
5768 const PDFNamedDest& rDest = m_aNamedDests[ nDestID ];
5769 // In order to correctly function both under an Internet browser and
5770 // directly with a reader (provided the reader has the feature) we
5771 // need to set the name of the destination the same way it will be encoded
5772 // in an Internet link
5773 INetURLObject aLocalURL( u"http://ahost.ax" ); //dummy location, won't be used
5774 aLocalURL.SetMark( rDest.m_aDestName );
5775
5776 const OUString aName = aLocalURL.GetMark( INetURLObject::DecodeMechanism::NONE ); //same coding as
5777 // in link creation ( see PDFWriterImpl::emitLinkAnnotations )
5778 const PDFPage& rDestPage = m_aPages[ rDest.m_nPage ];
5779
5780 aLine.append( '/' );
5781 appendDestinationName( aName, aLine ); // this conversion must be done when forming the link to target ( see in emitCatalog )
5782 aLine.append( '[' ); // the '[' can be emitted immediately, because the appendDestinationName function
5783 //maps the preceding character properly
5784 aLine.append( rDestPage.m_nPageObject );
5785 aLine.append( " 0 R" );
5786
5787 switch( rDest.m_eType )
5788 {
5790 default:
5791 aLine.append( "/XYZ " );
5792 appendFixedInt( rDest.m_aRect.Left(), aLine );
5793 aLine.append( ' ' );
5794 appendFixedInt( rDest.m_aRect.Bottom(), aLine );
5795 aLine.append( " 0" );
5796 break;
5798 aLine.append( "/FitR " );
5799 appendFixedInt( rDest.m_aRect.Left(), aLine );
5800 aLine.append( ' ' );
5801 appendFixedInt( rDest.m_aRect.Top(), aLine );
5802 aLine.append( ' ' );
5803 appendFixedInt( rDest.m_aRect.Right(), aLine );
5804 aLine.append( ' ' );
5805 appendFixedInt( rDest.m_aRect.Bottom(), aLine );
5806 break;
5807 }
5808 aLine.append( "]\n" );
5809 }
5810
5811 //close
5812 aLine.append( ">>\nendobj\n\n" );
5813 if( ! writeBuffer( aLine ) )
5814 nObject = 0;
5815 }
5816 else
5817 nObject = 0;
5818
5819 return nObject;
5820}
5821
5822// emits the output intent dictionary
5824{
5825 if( !m_bIsPDF_A1 && !m_bIsPDF_A2 && !m_bIsPDF_A3 )
5826 return 0;
5827
5828 //emit the sRGB standard profile, in ICC format, in a stream, per IEC61966-2.1
5829
5830 OStringBuffer aLine( 1024 );
5831 sal_Int32 nICCObject = createObject();
5832 sal_Int32 nStreamLengthObject = createObject();
5833
5834 aLine.append( nICCObject );
5835// sRGB has 3 colors, hence /N 3 below (PDF 1.4 table 4.16)
5836 aLine.append( " 0 obj\n<</N 3/Length " );
5837 aLine.append( nStreamLengthObject );
5838 aLine.append( " 0 R" );
5840 aLine.append( "/Filter/FlateDecode" );
5841 aLine.append( ">>\nstream\n" );
5842 if ( !updateObject( nICCObject ) ) return 0;
5843 if ( !writeBuffer( aLine ) ) return 0;
5844 //get file position
5845 sal_uInt64 nBeginStreamPos = 0;
5846 if (osl::File::E_None != m_aFile.getPos(nBeginStreamPos))
5847 return 0;
5849 checkAndEnableStreamEncryption( nICCObject );
5850 cmsHPROFILE hProfile = cmsCreate_sRGBProfile();
5851 //force ICC profile version 2.1
5852 cmsSetProfileVersion(hProfile, 2.1);
5853 cmsUInt32Number nBytesNeeded = 0;
5854 cmsSaveProfileToMem(hProfile, nullptr, &nBytesNeeded);
5855 if (!nBytesNeeded)
5856 return 0;
5857 std::vector<unsigned char> aBuffer(nBytesNeeded);
5858 cmsSaveProfileToMem(hProfile, aBuffer.data(), &nBytesNeeded);
5859 cmsCloseProfile(hProfile);
5860 bool written = writeBufferBytes( aBuffer.data(), static_cast<sal_Int32>(aBuffer.size()) );
5863
5864 sal_uInt64 nEndStreamPos = 0;
5865 if (m_aFile.getPos(nEndStreamPos) != osl::File::E_None)
5866 return 0;
5867
5868 if( !written )
5869 return 0;
5870 if( ! writeBuffer( "\nendstream\nendobj\n\n" ) )
5871 return 0 ;
5872 aLine.setLength( 0 );
5873
5874 //emit the stream length object
5875 if ( !updateObject( nStreamLengthObject ) ) return 0;
5876 aLine.setLength( 0 );
5877 aLine.append( nStreamLengthObject );
5878 aLine.append( " 0 obj\n" );
5879 aLine.append( static_cast<sal_Int64>(nEndStreamPos-nBeginStreamPos) );
5880 aLine.append( "\nendobj\n\n" );
5881 if ( !writeBuffer( aLine ) ) return 0;
5882 aLine.setLength( 0 );
5883
5884 //emit the OutputIntent dictionary
5885 sal_Int32 nOIObject = createObject();
5886 if ( !updateObject( nOIObject ) ) return 0;
5887 aLine.append( nOIObject );
5888 aLine.append( " 0 obj\n"
5889 "<</Type/OutputIntent/S/GTS_PDFA1/OutputConditionIdentifier");
5890
5891 appendLiteralStringEncrypt( std::string_view("sRGB IEC61966-2.1") ,nOIObject, aLine );
5892 aLine.append("/DestOutputProfile ");
5893 aLine.append( nICCObject );
5894 aLine.append( " 0 R>>\nendobj\n\n" );
5895 if ( !writeBuffer( aLine ) ) return 0;
5896
5897 return nOIObject;
5898}
5899
5900static void lcl_assignMeta(std::u16string_view aValue, OString& aMeta)
5901{
5902 if (!aValue.empty())
5903 {
5904 OUString aTempString = comphelper::string::encodeForXml(aValue);
5905 aMeta = OUStringToOString(aTempString, RTL_TEXTENCODING_UTF8);
5906 }
5907}
5908
5909// emits the document metadata
5911{
5913 return 0;
5914
5915 //get the object number for all the destinations
5916 sal_Int32 nObject = createObject();
5917
5918 if( updateObject( nObject ) )
5919 {
5920 pdf::XmpMetadata aMetadata;
5921
5922 if (m_bIsPDF_A1)
5923 aMetadata.mnPDF_A = 1;
5924 else if (m_bIsPDF_A2)
5925 aMetadata.mnPDF_A = 2;
5926 else if (m_bIsPDF_A3)
5927 aMetadata.mnPDF_A = 3;
5928
5929 aMetadata.mbPDF_UA = m_bIsPDF_UA;
5930
5938
5939 OStringBuffer aMetadataObj( 1024 );
5940
5941 aMetadataObj.append( nObject );
5942 aMetadataObj.append( " 0 obj\n" );
5943
5944 aMetadataObj.append( "<</Type/Metadata/Subtype/XML/Length " );
5945
5946 aMetadataObj.append( sal_Int32(aMetadata.getSize()) );
5947 aMetadataObj.append( ">>\nstream\n" );
5948 if ( !writeBuffer( aMetadataObj ) )
5949 return 0;
5950 //emit the stream
5951 if ( !writeBufferBytes( aMetadata.getData(), aMetadata.getSize() ) )
5952 return 0;
5953
5954 if( ! writeBuffer( "\nendstream\nendobj\n\n" ) )
5955 nObject = 0;
5956 }
5957 else
5958 nObject = 0;
5959
5960 return nObject;
5961}
5962
5964{
5965 // emit doc info
5966 sal_Int32 nDocInfoObject = emitInfoDict( );
5967
5968 sal_Int32 nSecObject = 0;
5969
5971 {
5972 //emit the security information
5973 //must be emitted as indirect dictionary object, since
5974 //Acrobat Reader 5 works only with this kind of implementation
5975 nSecObject = createObject();
5976
5977 if( updateObject( nSecObject ) )
5978 {
5979 OStringBuffer aLineS( 1024 );
5980 aLineS.append( nSecObject );
5981 aLineS.append( " 0 obj\n"
5982 "<</Filter/Standard/V " );
5983 // check the version
5984 aLineS.append( "2/Length 128/R 3" );
5985
5986 // emit the owner password, must not be encrypted
5987 aLineS.append( "/O(" );
5988 appendLiteralString( reinterpret_cast<char*>(m_aContext.Encryption.OValue.data()), sal_Int32(m_aContext.Encryption.OValue.size()), aLineS );
5989 aLineS.append( ")/U(" );
5990 appendLiteralString( reinterpret_cast<char*>(m_aContext.Encryption.UValue.data()), sal_Int32(m_aContext.Encryption.UValue.size()), aLineS );
5991 aLineS.append( ")/P " );// the permission set
5992 aLineS.append( m_nAccessPermissions );
5993 aLineS.append( ">>\nendobj\n\n" );
5994 if( !writeBuffer( aLineS ) )
5995 nSecObject = 0;
5996 }
5997 else
5998 nSecObject = 0;
5999 }
6000 // emit xref table
6001 // remember start
6002 sal_uInt64 nXRefOffset = 0;
6003 CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nXRefOffset )) );
6004 CHECK_RETURN( writeBuffer( "xref\n" ) );
6005
6006 sal_Int32 nObjects = m_aObjects.size();
6007 OStringBuffer aLine;
6008 aLine.append( "0 " );
6009 aLine.append( static_cast<sal_Int32>(nObjects+1) );
6010 aLine.append( "\n" );
6011 aLine.append( "0000000000 65535 f \n" );
6012 CHECK_RETURN( writeBuffer( aLine ) );
6013
6014 for( sal_Int32 i = 0; i < nObjects; i++ )
6015 {
6016 aLine.setLength( 0 );
6017 OString aOffset = OString::number( m_aObjects[i] );
6018 for( sal_Int32 j = 0; j < (10-aOffset.getLength()); j++ )
6019 aLine.append( '0' );
6020 aLine.append( aOffset );
6021 aLine.append( " 00000 n \n" );
6022 SAL_WARN_IF( aLine.getLength() != 20, "vcl.pdfwriter", "invalid xref entry" );
6023 CHECK_RETURN( writeBuffer( aLine ) );
6024 }
6025
6026 // prepare document checksum
6027 OStringBuffer aDocChecksum( 2*RTL_DIGEST_LENGTH_MD5+1 );
6028 ::std::vector<unsigned char> const nMD5Sum(m_DocDigest.finalize());
6029 for (sal_uInt8 i : nMD5Sum)
6030 appendHex( i, aDocChecksum );
6031 // document id set in setDocInfo method
6032 // emit trailer
6033 aLine.setLength( 0 );
6034 aLine.append( "trailer\n"
6035 "<</Size " );
6036 aLine.append( static_cast<sal_Int32>(nObjects+1) );
6037 aLine.append( "/Root " );
6038 aLine.append( m_nCatalogObject );
6039 aLine.append( " 0 R\n" );
6040 if( nSecObject )
6041 {
6042 aLine.append( "/Encrypt ");
6043 aLine.append( nSecObject );
6044 aLine.append( " 0 R\n" );
6045 }
6046 if( nDocInfoObject )
6047 {
6048 aLine.append( "/Info " );
6049 aLine.append( nDocInfoObject );
6050 aLine.append( " 0 R\n" );
6051 }
6053 {
6054 aLine.append( "/ID [ <" );
6055 for (auto const& item : m_aContext.Encryption.DocumentIdentifier)
6056 {
6057 appendHex( sal_Int8(item), aLine );
6058 }
6059 aLine.append( ">\n"
6060 "<" );
6061 for (auto const& item : m_aContext.Encryption.DocumentIdentifier)
6062 {
6063 appendHex( sal_Int8(item), aLine );
6064 }
6065 aLine.append( "> ]\n" );
6066 }
6067 if( !aDocChecksum.isEmpty() )
6068 {
6069 aLine.append( "/DocChecksum /" );
6070 aLine.append( aDocChecksum );
6071 aLine.append( "\n" );
6072 }
6073
6074 if (!m_aDocumentAttachedFiles.empty())
6075 {
6076 aLine.append( "/AdditionalStreams [" );
6077 for (auto const& rAttachedFile : m_aDocumentAttachedFiles)
6078 {
6079 aLine.append( "/" );
6080 appendName(rAttachedFile.maMimeType, aLine);
6081 aLine.append(" ");
6082 appendObjectReference(rAttachedFile.mnEmbeddedFileObjectId, aLine);
6083 aLine.append("\n");
6084 }
6085 aLine.append( "]\n" );
6086 }
6087
6088 aLine.append( ">>\n"
6089 "startxref\n" );
6090 aLine.append( static_cast<sal_Int64>(nXRefOffset) );
6091 aLine.append( "\n"
6092 "%%EOF\n" );
6093 return writeBuffer( aLine );
6094}
6095
6096namespace {
6097
6098struct AnnotationSortEntry
6099{
6100 sal_Int32 nTabOrder;
6101 sal_Int32 nObject;
6102 sal_Int32 nWidgetIndex;
6103
6104 AnnotationSortEntry( sal_Int32 nTab, sal_Int32 nObj, sal_Int32 nI ) :
6105 nTabOrder( nTab ),
6106 nObject( nObj ),
6107 nWidgetIndex( nI )
6108 {}
6109};
6110
6111struct AnnotSortContainer
6112{
6114 std::vector< AnnotationSortEntry > aSortedAnnots;
6115};
6116
6117struct AnnotSorterLess
6118{
6119 std::vector<PDFWidget>& m_rWidgets;
6120
6121 explicit AnnotSorterLess( std::vector<PDFWidget>& rWidgets ) : m_rWidgets( rWidgets ) {}
6122
6123 bool operator()( const AnnotationSortEntry& rLeft, const AnnotationSortEntry& rRight )
6124 {
6125 if( rLeft.nTabOrder < rRight.nTabOrder )
6126 return true;
6127 if( rRight.nTabOrder < rLeft.nTabOrder )
6128 return false;
6129 if( rLeft.nWidgetIndex < 0 && rRight.nWidgetIndex < 0 )
6130 return false;
6131 if( rRight.nWidgetIndex < 0 )
6132 return true;
6133 if( rLeft.nWidgetIndex < 0 )
6134 return false;
6135 // remember: widget rects are in PDF coordinates, so they are ordered down up
6136 if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() >
6137 m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() )
6138 return true;
6139 if( m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() >
6140 m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() )
6141 return false;
6142 if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Left() <
6143 m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Left() )
6144 return true;
6145 return false;
6146 }
6147};
6148
6149}
6150
6152{
6153 // sort widget annotations on each page as per their
6154 // TabOrder attribute
6155 std::unordered_map< sal_Int32, AnnotSortContainer > sorted;
6156 int nWidgets = m_aWidgets.size();
6157 for( int nW = 0; nW < nWidgets; nW++ )
6158 {
6159 const PDFWidget& rWidget = m_aWidgets[nW];
6160 if( rWidget.m_nPage >= 0 )
6161 {
6162 AnnotSortContainer& rCont = sorted[ rWidget.m_nPage ];
6163 // optimize vector allocation
6164 if( rCont.aSortedAnnots.empty() )
6165 rCont.aSortedAnnots.reserve( m_aPages[ rWidget.m_nPage ].m_aAnnotations.size() );
6166 // insert widget to tab sorter
6167 // RadioButtons are not page annotations, only their individual check boxes are
6168 if( rWidget.m_eType != PDFWriter::RadioButton )
6169 {
6170 rCont.aObjects.insert( rWidget.m_nObject );
6171 rCont.aSortedAnnots.emplace_back( rWidget.m_nTabOrder, rWidget.m_nObject, nW );
6172 }
6173 }
6174 }
6175 for (auto & item : sorted)
6176 {
6177 // append entries for non widget annotations
6178 PDFPage& rPage = m_aPages[ item.first ];
6179 unsigned int nAnnots = rPage.m_aAnnotations.size();
6180 for( unsigned int nA = 0; nA < nAnnots; nA++ )
6181 if( item.second.aObjects.find( rPage.m_aAnnotations[nA] ) == item.second.aObjects.end())
6182 item.second.aSortedAnnots.emplace_back( 10000, rPage.m_aAnnotations[nA], -1 );
6183
6184 AnnotSorterLess aLess( m_aWidgets );
6185 std::stable_sort( item.second.aSortedAnnots.begin(), item.second.aSortedAnnots.end(), aLess );
6186 // sanity check
6187 if( item.second.aSortedAnnots.size() == nAnnots)
6188 {
6189 for( unsigned int nA = 0; nA < nAnnots; nA++ )
6190 rPage.m_aAnnotations[nA] = item.second.aSortedAnnots[nA].nObject;
6191 }
6192 else
6193 {
6194 SAL_WARN( "vcl.pdfwriter", "wrong number of sorted annotations" );
6195 SAL_INFO("vcl.pdfwriter", "PDFWriterImpl::sortWidgets(): wrong number of sorted assertions "
6196 "on page nr " << item.first << ", " <<
6197 static_cast<tools::Long>(item.second.aSortedAnnots.size()) << " sorted and " <<
6198 static_cast<tools::Long>(nAnnots) << " unsorted");
6199 }
6200 }
6201
6202 // FIXME: implement tab order in structure tree for PDF 1.5
6203}
6204
6206{
6207 endPage();
6208
6209 // resort structure tree and annotations if necessary
6210 // needed for widget tab order
6211 sortWidgets();
6212
6213#if HAVE_FEATURE_NSS
6214 if( m_aContext.SignPDF )
6215 {
6216 // sign the document
6217 PDFWriter::SignatureWidget aSignature;
6218 aSignature.Name = "Signature1";
6219 createControl( aSignature, 0 );
6220 }
6221#endif
6222
6223 // emit catalog
6225
6226#if HAVE_FEATURE_NSS
6227 if (m_nSignatureObject != -1) // if document is signed, emit sigdict
6228 {
6229 if( !emitSignature() )
6230 {
6232 return false;
6233 }
6234 }
6235#endif
6236
6237 // emit trailer
6239
6240#if HAVE_FEATURE_NSS
6241 if (m_nSignatureObject != -1) // finalize the signature
6242 {
6243 if( !finalizeSignature() )
6244 {
6246 return false;
6247 }
6248 }
6249#endif
6250
6251 m_aFile.close();
6252 m_bOpen = false;
6253
6254 return true;
6255}
6256
6257
6258sal_Int32 PDFWriterImpl::getSystemFont( const vcl::Font& i_rFont )
6259{
6260 Push();
6261
6262 SetFont( i_rFont );
6263
6264 const LogicalFontInstance* pFontInstance = GetFontInstance();
6265 const vcl::font::PhysicalFontFace* pFace = pFontInstance->GetFontFace();
6266 sal_Int32 nFontID = 0;
6267 auto it = m_aSystemFonts.find( pFace );
6268 if( it != m_aSystemFonts.end() )
6269 nFontID = it->second.m_nNormalFontID;
6270 else
6271 {
6272 nFontID = m_nNextFID++;
6273 m_aSystemFonts[ pFace ] = EmbedFont();
6274 m_aSystemFonts[ pFace ].m_pFontInstance = const_cast<LogicalFontInstance*>(pFontInstance);
6275 m_aSystemFonts[ pFace ].m_nNormalFontID = nFontID;
6276 }
6277
6278 Pop();
6279 return nFontID;
6280}
6281
6283 const vcl::font::PhysicalFontFace* pFace,
6284 const std::vector<sal_Ucs>& rCodeUnits,
6285 sal_Int32 nGlyphWidth,
6286 sal_uInt8& nMappedGlyph,
6287 sal_Int32& nMappedFontObject)
6288{
6289 FontSubset& rSubset = m_aSubsets[ pFace ];
6290 // search for font specific glyphID
6291 auto it = rSubset.m_aMapping.find( nFontGlyphId );
6292 if( it != rSubset.m_aMapping.end() )
6293 {
6294 nMappedFontObject = it->second.m_nFontID;
6295 nMappedGlyph = it->second.m_nSubsetGlyphID;
6296 }
6297 else
6298 {
6299 // create new subset if necessary
6300 if( rSubset.m_aSubsets.empty()
6301 || (rSubset.m_aSubsets.back().m_aMapping.size() > 254) )
6302 {
6303 rSubset.m_aSubsets.emplace_back( m_nNextFID++ );
6304 }
6305
6306 // copy font id
6307 nMappedFontObject = rSubset.m_aSubsets.back().m_nFontID;
6308 // create new glyph in subset
6309 sal_uInt8 nNewId = sal::static_int_cast<sal_uInt8>(rSubset.m_aSubsets.back().m_aMapping.size()+1);
6310 nMappedGlyph = nNewId;
6311
6312 // add new glyph to emitted font subset
6313 GlyphEmit& rNewGlyphEmit = rSubset.m_aSubsets.back().m_aMapping[ nFontGlyphId ];
6314 rNewGlyphEmit.setGlyphId( nNewId );
6315 rNewGlyphEmit.setGlyphWidth(XUnits(pFace->UnitsPerEm(), nGlyphWidth));
6316 for (const auto nCode : rCodeUnits)
6317 rNewGlyphEmit.addCode(nCode);
6318
6319 // add new glyph to font mapping
6320 Glyph& rNewGlyph = rSubset.m_aMapping[ nFontGlyphId ];
6321 rNewGlyph.m_nFontID = nMappedFontObject;
6322 rNewGlyph.m_nSubsetGlyphID = nNewId;
6323 }
6324}
6325
6327 const vcl::font::PhysicalFontFace* pFace,
6328 const LogicalFontInstance* pFont,
6329 const std::vector<sal_Ucs>& rCodeUnits, sal_Int32 nGlyphWidth,
6330 sal_uInt8& nMappedGlyph, sal_Int32& nMappedFontObject)
6331{
6332 auto bVariations = !pFace->GetVariations(*pFont).empty();
6333 // tdf#155161
6334 // PDF doesn’t support CFF2 table and we currently don’t convert them to
6335 // Type 1 (like we do with CFF table), so treat it like fonts with
6336 // variations and embed as Type 3 fonts.
6337 if (!pFace->GetRawFontData(HB_TAG('C', 'F', 'F', '2')).empty())
6338 bVariations = true;
6339
6340 if (pFace->IsColorFont() || bVariations)
6341 {
6342 // Font has colors, check if this glyph has color layers or bitmap.
6343 tools::Rectangle aRect;
6344 auto aLayers = pFace->GetGlyphColorLayers(nFontGlyphId);
6345 auto aBitmap = pFace->GetGlyphColorBitmap(nFontGlyphId, aRect);
6346 if (!aLayers.empty() || !aBitmap.empty() || bVariations)
6347 {
6348 auto& rSubset = m_aType3Fonts[pFace];
6349 auto it = rSubset.m_aMapping.find(nFontGlyphId);
6350 if (it != rSubset.m_aMapping.end())
6351 {
6352 nMappedFontObject = it->second.m_nFontID;
6353 nMappedGlyph = it->second.m_nSubsetGlyphID;
6354 }
6355 else
6356 {
6357 // create new subset if necessary
6358 if (rSubset.m_aSubsets.empty()
6359 || (rSubset.m_aSubsets.back().m_aMapping.size() > 254))
6360 {
6361 rSubset.m_aSubsets.emplace_back(m_nNextFID++);
6362 }
6363
6364 // copy font id
6365 nMappedFontObject = rSubset.m_aSubsets.back().m_nFontID;
6366 // create new glyph in subset
6367 sal_uInt8 nNewId = sal::static_int_cast<sal_uInt8>(
6368 rSubset.m_aSubsets.back().m_aMapping.size() + 1);
6369 nMappedGlyph = nNewId;
6370
6371 // add new glyph to emitted font subset
6372 auto& rNewGlyphEmit = rSubset.m_aSubsets.back().m_aMapping[nFontGlyphId];
6373 rNewGlyphEmit.setGlyphId(nNewId);
6374 rNewGlyphEmit.setGlyphWidth(nGlyphWidth);
6375 for (const auto nCode : rCodeUnits)
6376 rNewGlyphEmit.addCode(nCode);
6377
6378 // add color layers to the glyphs
6379 if (!aLayers.empty())
6380 {
6381 for (const auto& aLayer : aLayers)
6382 {
6383 sal_uInt8 nLayerGlyph;
6384 sal_Int32 nLayerFontID;
6385 registerSimpleGlyph(aLayer.nGlyphIndex, pFace, rCodeUnits, nGlyphWidth,
6386 nLayerGlyph, nLayerFontID);
6387
6388 rNewGlyphEmit.addColorLayer(
6389 { nLayerFontID, nLayerGlyph, aLayer.nColorIndex });
6390 }
6391 }
6392 else if (!aBitmap.empty())
6393 rNewGlyphEmit.setColorBitmap(aBitmap, aRect);
6394 else if (bVariations)
6395 rNewGlyphEmit.setOutline(pFont->GetGlyphOutlineUntransformed(nFontGlyphId));
6396
6397 // add new glyph to font mapping
6398 Glyph& rNewGlyph = rSubset.m_aMapping[nFontGlyphId];
6399 rNewGlyph.m_nFontID = nMappedFontObject;
6400 rNewGlyph.m_nSubsetGlyphID = nNewId;
6401 }
6402 return;
6403 }
6404 }
6405
6406 // If we reach here then the glyph has no color layers.
6407 registerSimpleGlyph(nFontGlyphId, pFace, rCodeUnits, nGlyphWidth, nMappedGlyph,
6408 nMappedFontObject);
6409}
6410
6411void PDFWriterImpl::drawRelief( SalLayout& rLayout, const OUString& rText, bool bTextLines )
6412{
6414
6416
6418 Color aTextLineColor = m_aCurrentPDFState.m_aTextLineColor;
6419 Color aOverlineColor = m_aCurrentPDFState.m_aOverlineColor;
6420 Color aReliefColor( COL_LIGHTGRAY );
6421 if( aTextColor == COL_BLACK )
6422 aTextColor = COL_WHITE;
6423 if( aTextLineColor == COL_BLACK )
6424 aTextLineColor = COL_WHITE;
6425 if( aOverlineColor == COL_BLACK )
6426 aOverlineColor = COL_WHITE;
6427 // coverity[copy_paste_error : FALSE] - aReliefColor depending on aTextColor is correct
6428 if( aTextColor == COL_WHITE )
6429 aReliefColor = COL_BLACK;
6430
6431 Font aSetFont = m_aCurrentPDFState.m_aFont;
6432 aSetFont.SetRelief( FontRelief::NONE );
6433 aSetFont.SetShadow( false );
6434
6435 aSetFont.SetColor( aReliefColor );
6436 setTextLineColor( aReliefColor );
6437 setOverlineColor( aReliefColor );
6438 setFont( aSetFont );
6439 tools::Long nOff = 1 + GetDPIX()/300;
6440 if( eRelief == FontRelief::Engraved )
6441 nOff = -nOff;
6442
6443 rLayout.DrawOffset() += Point( nOff, nOff );
6445 drawLayout( rLayout, rText, bTextLines );
6446
6447 rLayout.DrawOffset() -= Point( nOff, nOff );
6448 setTextLineColor( aTextLineColor );
6449 setOverlineColor( aOverlineColor );
6450 aSetFont.SetColor( aTextColor );
6451 setFont( aSetFont );
6453 drawLayout( rLayout, rText, bTextLines );
6454
6455 // clean up the mess
6456 pop();
6457}
6458
6459void PDFWriterImpl::drawShadow( SalLayout& rLayout, const OUString& rText, bool bTextLines )
6460{
6461 Font aSaveFont = m_aCurrentPDFState.m_aFont;
6462 Color aSaveTextLineColor = m_aCurrentPDFState.m_aTextLineColor;
6463 Color aSaveOverlineColor = m_aCurrentPDFState.m_aOverlineColor;
6464
6466 if( rFont.GetColor() == COL_BLACK || rFont.GetColor().GetLuminance() < 8 )
6467 rFont.SetColor( COL_LIGHTGRAY );
6468 else
6469 rFont.SetColor( COL_BLACK );
6470 rFont.SetShadow( false );
6471 rFont.SetOutline( false );
6472 setFont( rFont );
6473 setTextLineColor( rFont.GetColor() );
6474 setOverlineColor( rFont.GetColor() );
6476
6477 tools::Long nOff = 1 + ((GetFontInstance()->mnLineHeight-24)/24);
6478 if( rFont.IsOutline() )
6479 nOff++;
6480 rLayout.DrawBase() += basegfx::B2DPoint(nOff, nOff);
6481 drawLayout( rLayout, rText, bTextLines );
6482 rLayout.DrawBase() -= basegfx::B2DPoint(nOff, nOff);
6483
6484 setFont( aSaveFont );
6485 setTextLineColor( aSaveTextLineColor );
6486 setOverlineColor( aSaveOverlineColor );
6488}
6489
6491 const std::vector<PDFGlyph>& rGlyphs,
6492 OStringBuffer& rLine,
6493 const Point& rAlignOffset,
6494 const Matrix3& rRotScale,
6495 double fAngle,
6496 double fXScale,
6497 sal_Int32 nFontHeight)
6498{
6499 double nXOffset = 0;
6500 Point aCurPos(SubPixelToLogic(rGlyphs[0].m_aPos));
6501 aCurPos += rAlignOffset;
6502 for( size_t i = 0; i < rGlyphs.size(); i++ )
6503 {
6504 // have to emit each glyph on its own
6505 double fDeltaAngle = 0.0;
6506 double fYScale = 1.0;
6507 double fTempXScale = fXScale;
6508
6509 // perform artificial italics if necessary
6510 double fSkew = 0.0;
6511 if (rGlyphs[i].m_pFont->NeedsArtificialItalic())
6512 fSkew = ARTIFICIAL_ITALIC_SKEW;
6513
6514 double fSkewB = fSkew;
6515 double fSkewA = 0.0;
6516
6517 Point aDeltaPos;
6518 if (rGlyphs[i].m_pGlyph->IsVertical())
6519 {
6520 fDeltaAngle = M_PI/2.0;
6521 fYScale = fXScale;
6522 fTempXScale = 1.0;
6523 fSkewA = -fSkewB;
6524 fSkewB = 0.0;
6525 }
6526 aDeltaPos += SubPixelToLogic(basegfx::B2DPoint(nXOffset / fXScale, 0)) - SubPixelToLogic(basegfx::B2DPoint());
6527 if( i < rGlyphs.size()-1 )
6528 // #i120627# the text on the Y axis is reversed when export ppt file to PDF format
6529 {
6530 double nOffsetX = rGlyphs[i+1].m_aPos.getX() - rGlyphs[i].m_aPos.getX();
6531 double nOffsetY = rGlyphs[i+1].m_aPos.getY() - rGlyphs[i].m_aPos.getY();
6532 nXOffset += std::hypot(nOffsetX, nOffsetY);
6533 }
6534 if (!rGlyphs[i].m_pGlyph->glyphId())
6535 continue;
6536
6537 aDeltaPos = rRotScale.transform( aDeltaPos );
6538
6539 Matrix3 aMat;
6540 if( fSkewB != 0.0 || fSkewA != 0.0 )
6541 aMat.skew( fSkewA, fSkewB );
6542 aMat.scale( fTempXScale, fYScale );
6543 aMat.rotate( fAngle+fDeltaAngle );
6544 aMat.translate( aCurPos.X()+aDeltaPos.X(), aCurPos.Y()+aDeltaPos.Y() );
6545 m_aPages.back().appendMatrix3(aMat, rLine);
6546 rLine.append( " Tm" );
6547 if( i == 0 || rGlyphs[i-1].m_nMappedFontId != rGlyphs[i].m_nMappedFontId )
6548 {
6549 rLine.append( " /F" );
6550 rLine.append( rGlyphs[i].m_nMappedFontId );
6551 rLine.append( ' ' );
6552 m_aPages.back().appendMappedLength( nFontHeight, rLine );
6553 rLine.append( " Tf" );
6554 }
6555 rLine.append( "<" );
6556 appendHex( rGlyphs[i].m_nMappedGlyphId, rLine );
6557 rLine.append( ">Tj\n" );
6558 }
6559}
6560
6562 const std::vector<PDFGlyph>& rGlyphs,
6563 OStringBuffer& rLine,
6564 const Point& rAlignOffset,
6565 bool bFirst,
6566 double fAngle,
6567 double fXScale,
6568 sal_Int32 nFontHeight,
6569 sal_Int32 nPixelFontHeight)
6570{
6571 // horizontal (= normal) case
6572
6573 // fill in run end indices
6574 // end is marked by index of the first glyph of the next run
6575 // a run is marked by same mapped font id and same Y position
6576 std::vector< sal_uInt32 > aRunEnds;
6577 aRunEnds.reserve( rGlyphs.size() );
6578 for( size_t i = 1; i < rGlyphs.size(); i++ )
6579 {
6580 if( rGlyphs[i].m_nMappedFontId != rGlyphs[i-1].m_nMappedFontId ||
6581 rGlyphs[i].m_pFont != rGlyphs[i-1].m_pFont ||
6582 rGlyphs[i].m_aPos.getY() != rGlyphs[i-1].m_aPos.getY() )
6583 {
6584 aRunEnds.push_back(i);
6585 }
6586 }
6587 // last run ends at last glyph
6588 aRunEnds.push_back( rGlyphs.size() );
6589
6590 // loop over runs of the same font
6591 sal_uInt32 nBeginRun = 0;
6592 for( size_t nRun = 0; nRun < aRunEnds.size(); nRun++ )
6593 {
6594 // setup text matrix back transformed to current coordinate system
6595 Point aCurPos(SubPixelToLogic(rGlyphs[nBeginRun].m_aPos));
6596 aCurPos += rAlignOffset;
6597
6598 // perform artificial italics if necessary
6599 double fSkew = 0.0;
6600 if (rGlyphs[nBeginRun].m_pFont->NeedsArtificialItalic())
6601 fSkew = ARTIFICIAL_ITALIC_SKEW;
6602
6603 // the first run can be set with "Td" operator
6604 // subsequent use of that operator would move
6605 // the textline matrix relative to what was set before
6606 // making use of that would drive us into rounding issues
6607 Matrix3 aMat;
6608 if( bFirst && nRun == 0 && fAngle == 0.0 && fXScale == 1.0 && fSkew == 0.0 )
6609 {
6610 m_aPages.back().appendPoint( aCurPos, rLine );
6611 rLine.append( " Td " );
6612 }
6613 else
6614 {
6615 if( fSkew != 0.0 )
6616 aMat.skew( 0.0, fSkew );
6617 aMat.scale( fXScale, 1.0 );
6618 aMat.rotate( fAngle );
6619 aMat.translate( aCurPos.X(), aCurPos.Y() );
6620 m_aPages.back().appendMatrix3(aMat, rLine);
6621 rLine.append( " Tm\n" );
6622 }
6623 // set up correct font
6624 rLine.append( "/F" );
6625 rLine.append( rGlyphs[nBeginRun].m_nMappedFontId );
6626 rLine.append( ' ' );
6627 m_aPages.back().appendMappedLength( nFontHeight, rLine );
6628 rLine.append( " Tf" );
6629
6630 // output glyphs using Tj or TJ
6631 OStringBuffer aKernedLine( 256 ), aUnkernedLine( 256 );
6632 aKernedLine.append( "[<" );
6633 aUnkernedLine.append( '<' );
6634 appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aKernedLine );
6635 appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aUnkernedLine );
6636
6637 aMat.invert();
6638 bool bNeedKern = false;
6639 for( sal_uInt32 nPos = nBeginRun+1; nPos < aRunEnds[nRun]; nPos++ )
6640 {
6641 appendHex( rGlyphs[nPos].m_nMappedGlyphId, aUnkernedLine );
6642 // check if default glyph positioning is sufficient
6643 const basegfx::B2DPoint aThisPos = aMat.transform( rGlyphs[nPos].m_aPos );
6644 const basegfx::B2DPoint aPrevPos = aMat.transform( rGlyphs[nPos-1].m_aPos );
6645 double fAdvance = aThisPos.getX() - aPrevPos.getX();
6646 fAdvance *= 1000.0 / nPixelFontHeight;
6647 const double fAdjustment = rGlyphs[nPos-1].m_nNativeWidth - fAdvance + 0.5;
6649 fAdjustment < SAL_MIN_INT32 || fAdjustment > SAL_MAX_INT32, "vcl.pdfwriter",
6650 "adjustment " << fAdjustment << " outside 32-bit int");
6651 const sal_Int32 nAdjustment = static_cast<sal_Int32>(
6652 std::clamp(fAdjustment, double(SAL_MIN_INT32), double(SAL_MAX_INT32)));
6653 if( nAdjustment != 0 )
6654 {
6655 // apply individual glyph positioning
6656 bNeedKern = true;
6657 aKernedLine.append( ">" );
6658 aKernedLine.append( nAdjustment );
6659 aKernedLine.append( "<" );
6660 }
6661 appendHex( rGlyphs[nPos].m_nMappedGlyphId, aKernedLine );
6662 }
6663 aKernedLine.append( ">]TJ\n" );
6664 aUnkernedLine.append( ">Tj\n" );
6665 rLine.append( bNeedKern ? aKernedLine : aUnkernedLine );
6666
6667 // set beginning of next run
6668 nBeginRun = aRunEnds[nRun];
6669 }
6670}
6671
6672void PDFWriterImpl::drawLayout( SalLayout& rLayout, const OUString& rText, bool bTextLines )
6673{
6674 // relief takes precedence over shadow (see outdev3.cxx)
6676 {
6677 drawRelief( rLayout, rText, bTextLines );
6678 return;
6679 }
6681 drawShadow( rLayout, rText, bTextLines );
6682
6683 OStringBuffer aLine( 512 );
6684
6685 const int nMaxGlyphs = 256;
6686
6687 std::vector<sal_Ucs> aCodeUnits;
6688 bool bVertical = m_aCurrentPDFState.m_aFont.IsVertical();
6689 int nIndex = 0;
6690 double fXScale = 1.0;
6691 sal_Int32 nPixelFontHeight = GetFontInstance()->GetFontSelectPattern().mnHeight;
6693
6694 // transform font height back to current units
6695 // note: the layout calculates in outdevs device pixel !!
6696 sal_Int32 nFontHeight = ImplDevicePixelToLogicHeight( nPixelFontHeight );
6698 {
6700 aFont.SetAverageFontWidth( 0 );
6701 FontMetric aMetric = GetFontMetric( aFont );
6703 {
6704 fXScale =
6705 static_cast<double>(m_aCurrentPDFState.m_aFont.GetAverageFontWidth()) /
6706 static_cast<double>(aMetric.GetAverageFontWidth());
6707 }
6708 }
6709
6710 // if the mapmode is distorted we need to adjust for that also
6712 {
6714 }
6715
6717 // normalize angles
6718 while( nAngle < 0_deg10 )
6719 nAngle += 3600_deg10;
6720 nAngle = nAngle % 3600_deg10;
6721 double fAngle = toRadians(nAngle);
6722
6723 Matrix3 aRotScale;
6724 aRotScale.scale( fXScale, 1.0 );
6725 if( fAngle != 0.0 )
6726 aRotScale.rotate( -fAngle );
6727
6728 bool bPop = false;
6729 bool bABold = false;
6730 // artificial bold necessary ?
6731 if (GetFontInstance()->NeedsArtificialBold())
6732 {
6733 aLine.append("q ");
6734 bPop = true;
6735 bABold = true;
6736 }
6737 // setup text colors (if necessary)
6738 Color aStrokeColor( COL_TRANSPARENT );
6739 Color aNonStrokeColor( COL_TRANSPARENT );
6740
6742 {
6743 aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
6744 aNonStrokeColor = COL_WHITE;
6745 }
6746 else
6747 aNonStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
6748 if( bABold )
6749 aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
6750
6751 if( aStrokeColor != COL_TRANSPARENT && aStrokeColor != m_aCurrentPDFState.m_aLineColor )
6752 {
6753 if( ! bPop )
6754 aLine.append( "q " );
6755 bPop = true;
6756 appendStrokingColor( aStrokeColor, aLine );
6757 aLine.append( "\n" );
6758 }
6759 if( aNonStrokeColor != COL_TRANSPARENT && aNonStrokeColor != m_aCurrentPDFState.m_aFillColor )
6760 {
6761 if( ! bPop )
6762 aLine.append( "q " );
6763 bPop = true;
6764 appendNonStrokingColor( aNonStrokeColor, aLine );
6765 aLine.append( "\n" );
6766 }
6767
6768 // begin text object
6769 aLine.append( "BT\n" );
6770 // outline attribute ?
6771 if( m_aCurrentPDFState.m_aFont.IsOutline() || bABold )
6772 {
6773 // set correct text mode, set stroke width
6774 aLine.append( "2 Tr " ); // fill, then stroke
6775
6777 {
6778 // unclear what to do in case of outline and artificial bold
6779 // for the time being outline wins
6780 aLine.append( "0.25 w \n" );
6781 }
6782 else
6783 {
6784 double fW = static_cast<double>(m_aCurrentPDFState.m_aFont.GetFontHeight()) / 30.0;
6785 m_aPages.back().appendMappedLength( fW, aLine );
6786 aLine.append ( " w\n" );
6787 }
6788 }
6789
6790 FontMetric aRefDevFontMetric = GetFontMetric();
6791 const GlyphItem* pGlyph = nullptr;
6792 const LogicalFontInstance* pGlyphFont = nullptr;
6793
6794 // collect the glyphs into a single array
6795 std::vector< PDFGlyph > aGlyphs;
6796 aGlyphs.reserve( nMaxGlyphs );
6797 // first get all the glyphs and register them; coordinates still in Pixel
6798 basegfx::B2DPoint aPos;
6799 while (rLayout.GetNextGlyph(&pGlyph, aPos, nIndex, &pGlyphFont))
6800 {
6801 const auto* pFace = pGlyphFont->GetFontFace();
6802
6803 aCodeUnits.clear();
6804
6805 // tdf#66597, tdf#115117
6806 //
6807 // Here is how we embed textual content in PDF files, to allow for
6808 // better text extraction for complex and typography-rich text.
6809 //
6810 // * If there is many to one or many to many mapping, use an
6811 // ActualText span embedding the original string, since ToUnicode
6812 // can't handle these.
6813 // * If the one glyph is used for several Unicode code points, also
6814 // use ActualText since ToUnicode can map each glyph in the font
6815 // only once.
6816 // * Limit ActualText to single cluster at a time, since using it
6817 // for whole words or sentences breaks text selection and
6818 // highlighting in PDF viewers (there will be no way to tell
6819 // which glyphs belong to which characters).
6820 // * Keep generating (now) redundant ToUnicode entries for
6821 // compatibility with old tools not supporting ActualText.
6822
6823 assert(pGlyph->charCount() >= 0);
6824 for (int n = 0; n < pGlyph->charCount(); n++)
6825 aCodeUnits.push_back(rText[pGlyph->charPos() + n]);
6826
6827 bool bUseActualText = false;
6828
6829 // If this is a start of complex cluster, use ActualText.
6830 if (pGlyph->IsClusterStart())
6831 bUseActualText = true;
6832
6833 const auto nGlyphId = pGlyph->glyphId();
6834
6835 // A glyph can't have more than one ToUnicode entry, use ActualText
6836 // instead.
6837 if (!aCodeUnits.empty() && !bUseActualText)
6838 {
6839 for (const auto& rSubset : m_aSubsets[pFace].m_aSubsets)
6840 {
6841 const auto& it = rSubset.m_aMapping.find(nGlyphId);
6842 if (it != rSubset.m_aMapping.cend() && it->second.codes() != aCodeUnits)
6843 {
6844 bUseActualText = true;
6845 aCodeUnits.clear();
6846 }
6847 }
6848 }
6849
6850 auto nGlyphWidth = pGlyphFont->GetGlyphWidth(nGlyphId, pGlyph->IsVertical(), false);
6851
6852 sal_uInt8 nMappedGlyph;
6853 sal_Int32 nMappedFontObject;
6854 registerGlyph(nGlyphId, pFace, pGlyphFont, aCodeUnits, nGlyphWidth, nMappedGlyph, nMappedFontObject);
6855
6856 int nCharPos = -1;
6857 if (bUseActualText || pGlyph->IsInCluster())
6858 nCharPos = pGlyph->charPos();
6859
6860 aGlyphs.emplace_back(aPos,
6861 pGlyph,
6862 pGlyphFont,
6863 XUnits(pFace->UnitsPerEm(), nGlyphWidth),
6864 nMappedFontObject,
6865 nMappedGlyph,
6866 nCharPos);
6867 }
6868
6869 // Avoid fill color when map mode is in pixels, the below code assumes
6870 // logic map mode.
6871 bool bPixel = m_aCurrentPDFState.m_aMapMode.GetMapUnit() == MapUnit::MapPixel;
6873 {
6874 // PDF doesn't have a text fill color, so draw a rectangle before
6875 // drawing the actual text.
6878 // Avoid border around the rectangle for Writer shape text.
6880
6881 // The rectangle is the bounding box of the text, but also includes
6882 // ascent / descent to match the on-screen rendering.
6883 // This is the top left of the text without ascent / descent.
6884 basegfx::B2DPoint aDrawPosition(rLayout.GetDrawPosition());
6885 tools::Rectangle aRectangle(SubPixelToLogic(aDrawPosition),
6887 aRectangle.AdjustTop(-aRefDevFontMetric.GetAscent());
6888 // This includes ascent / descent.
6889 aRectangle.setHeight(aRefDevFontMetric.GetLineHeight());
6890
6891 const LogicalFontInstance* pFontInstance = GetFontInstance();
6892 if (pFontInstance->mnOrientation)
6893 {
6894 // Adapt rectangle for rotated text.
6895 tools::Polygon aPolygon(aRectangle);
6896 aPolygon.Rotate(SubPixelToLogic(aDrawPosition), pFontInstance->mnOrientation);
6897 drawPolygon(aPolygon);
6898 }
6899 else
6900 drawRectangle(aRectangle);
6901
6902 pop();
6903 }
6904
6905 Point aAlignOffset;
6906 if ( eAlign == ALIGN_BOTTOM )
6907 aAlignOffset.AdjustY( -(aRefDevFontMetric.GetDescent()) );
6908 else if ( eAlign == ALIGN_TOP )
6909 aAlignOffset.AdjustY(aRefDevFontMetric.GetAscent() );
6910 if( aAlignOffset.X() || aAlignOffset.Y() )
6911 aAlignOffset = aRotScale.transform( aAlignOffset );
6912
6913 /* #159153# do not emit an empty glyph vector; this can happen if e.g. the original
6914 string contained only one of the UTF16 BOMs
6915 */
6916 if( ! aGlyphs.empty() )
6917 {
6918 size_t nStart = 0;
6919 size_t nEnd = 0;
6920 while (nStart < aGlyphs.size())
6921 {
6922 while (nEnd < aGlyphs.size() && aGlyphs[nEnd].m_nCharPos == aGlyphs[nStart].m_nCharPos)
6923 nEnd++;
6924
6925 std::vector<PDFGlyph> aRun(aGlyphs.begin() + nStart, aGlyphs.begin() + nEnd);
6926
6927 int nCharPos, nCharCount;
6928 if (!aRun.front().m_pGlyph->IsRTLGlyph())
6929 {
6930 nCharPos = aRun.front().m_nCharPos;
6931 nCharCount = aRun.front().m_pGlyph->charCount();
6932 }
6933 else
6934 {
6935 nCharPos = aRun.back().m_nCharPos;
6936 nCharCount = aRun.back().m_pGlyph->charCount();
6937 }
6938
6939 if (nCharPos >= 0 && nCharCount)
6940 {
6941 aLine.append("/Span<</ActualText<FEFF");
6942 for (int i = 0; i < nCharCount; i++)
6943 {
6944 sal_Unicode aChar = rText[nCharPos + i];
6945 appendHex(static_cast<sal_Int8>(aChar >> 8), aLine);
6946 appendHex(static_cast<sal_Int8>(aChar & 255), aLine);
6947 }
6948 aLine.append( ">>>\nBDC\n" );
6949 }
6950
6951 if (bVertical)
6952 drawVerticalGlyphs(aRun, aLine, aAlignOffset, aRotScale, fAngle, fXScale, nFontHeight);
6953 else
6954 drawHorizontalGlyphs(aRun, aLine, aAlignOffset, nStart == 0, fAngle, fXScale, nFontHeight, nPixelFontHeight);
6955
6956 if (nCharPos >= 0 && nCharCount)
6957 aLine.append( "EMC\n" );
6958
6959 nStart = nEnd;
6960 }
6961 }
6962
6963 // end textobject
6964 aLine.append( "ET\n" );
6965 if( bPop )
6966 aLine.append( "Q\n" );
6967
6968 writeBuffer( aLine );
6969
6970 // draw eventual textlines
6974 if( bTextLines &&
6975 (
6976 ( eUnderline != LINESTYLE_NONE && eUnderline != LINESTYLE_DONTKNOW ) ||
6977 ( eOverline != LINESTYLE_NONE && eOverline != LINESTYLE_DONTKNOW ) ||
6978 ( eStrikeout != STRIKEOUT_NONE && eStrikeout != STRIKEOUT_DONTKNOW )
6979 )
6980 )
6981 {
6982 bool bUnderlineAbove = m_aCurrentPDFState.m_aFont.IsUnderlineAbove();
6984 {
6985 basegfx::B2DPoint aStartPt;
6986 double nWidth = 0;
6987 nIndex = 0;
6988 while (rLayout.GetNextGlyph(&pGlyph, aPos, nIndex))
6989 {
6990 if (!pGlyph->IsSpacing())
6991 {
6992 if( !nWidth )
6993 aStartPt = aPos;
6994
6995 nWidth += pGlyph->newWidth();
6996 }
6997 else if( nWidth > 0 )
6998 {
6999 drawTextLine( SubPixelToLogic(aStartPt),
7001 eStrikeout, eUnderline, eOverline, bUnderlineAbove );
7002 nWidth = 0;
7003 }
7004 }
7005
7006 if( nWidth > 0 )
7007 {
7008 drawTextLine( SubPixelToLogic(aStartPt),
7010 eStrikeout, eUnderline, eOverline, bUnderlineAbove );
7011 }
7012 }
7013 else
7014 {
7015 basegfx::B2DPoint aStartPt = rLayout.GetDrawPosition();
7016 int nWidth = rLayout.GetTextWidth();
7017 drawTextLine( SubPixelToLogic(aStartPt),
7019 eStrikeout, eUnderline, eOverline, bUnderlineAbove );
7020 }
7021 }
7022
7023 // write eventual emphasis marks
7024 if( !(m_aCurrentPDFState.m_aFont.GetEmphasisMark() & FontEmphasisMark::Style) )
7025 return;
7026
7028
7029 aLine.setLength( 0 );
7030 aLine.append( "q\n" );
7031
7033
7034 tools::Long nEmphHeight;
7035 if ( nEmphMark & FontEmphasisMark::PosBelow )
7036 nEmphHeight = GetEmphasisDescent();
7037 else
7038 nEmphHeight = GetEmphasisAscent();
7039
7040 vcl::font::EmphasisMark aEmphasisMark(nEmphMark, ImplDevicePixelToLogicWidth(nEmphHeight), GetDPIY());
7041 if ( aEmphasisMark.IsShapePolyLine() )
7042 {
7045 }
7046 else
7047 {
7050 }
7051
7052 writeBuffer( aLine );
7053
7054 Point aOffset(0,0);
7055 Point aOffsetVert(0,0);
7056
7057 if ( nEmphMark & FontEmphasisMark::PosBelow )
7058 {
7059 aOffset.AdjustY(GetFontInstance()->mxFontMetric->GetDescent() + aEmphasisMark.GetYOffset() );
7060 aOffsetVert = aOffset;
7061 }
7062 else
7063 {
7064 aOffset.AdjustY( -(GetFontInstance()->mxFontMetric->GetAscent() + aEmphasisMark.GetYOffset()) );
7065 // Todo: use ideographic em-box or ideographic character face information.
7066 aOffsetVert.AdjustY(-(GetFontInstance()->mxFontMetric->GetAscent() +
7067 GetFontInstance()->mxFontMetric->GetDescent() + aEmphasisMark.GetYOffset()));
7068 }
7069
7070 tools::Long nEmphWidth2 = aEmphasisMark.GetWidth() / 2;
7071 tools::Long nEmphHeight2 = nEmphHeight / 2;
7072 aOffset += Point( nEmphWidth2, nEmphHeight2 );
7073
7074 if ( eAlign == ALIGN_BOTTOM )
7075 aOffset.AdjustY( -(GetFontInstance()->mxFontMetric->GetDescent()) );
7076 else if ( eAlign == ALIGN_TOP )
7077 aOffset.AdjustY(GetFontInstance()->mxFontMetric->GetAscent() );
7078
7079 tools::Rectangle aRectangle;
7080 nIndex = 0;
7081 while (rLayout.GetNextGlyph(&pGlyph, aPos, nIndex, &pGlyphFont))
7082 {
7083 if (!pGlyph->GetGlyphBoundRect(pGlyphFont, aRectangle))
7084 continue;
7085
7086 if (!pGlyph->IsSpacing())
7087 {
7088 basegfx::B2DPoint aAdjOffset;
7089 if (pGlyph->IsVertical())
7090 {
7091 aAdjOffset = basegfx::B2DPoint(aOffsetVert.X(), aOffsetVert.Y());
7092 aAdjOffset.adjustX((-pGlyph->origWidth() + aEmphasisMark.GetWidth()) / 2);
7093 }
7094 else
7095 {
7096 aAdjOffset = basegfx::B2DPoint(aOffset.X(), aOffset.Y());
7097 aAdjOffset.adjustX(aRectangle.Left() + (aRectangle.GetWidth() - aEmphasisMark.GetWidth()) / 2 );
7098 }
7099
7100 aAdjOffset = aRotScale.transform( aAdjOffset );
7101
7102 aAdjOffset -= basegfx::B2DPoint(nEmphWidth2, nEmphHeight2);
7103
7104 basegfx::B2DPoint aMarkDevPos(aPos);
7105 aMarkDevPos += aAdjOffset;
7106 Point aMarkPos = SubPixelToLogic(aMarkDevPos);
7107 drawEmphasisMark( aMarkPos.X(), aMarkPos.Y(),
7108 aEmphasisMark.GetShape(), aEmphasisMark.IsShapePolyLine(),
7109 aEmphasisMark.GetRect1(), aEmphasisMark.GetRect2() );
7110 }
7111 }
7112
7113 writeBuffer( "Q\n" );
7114 pop();
7115
7116}
7117
7119 const tools::PolyPolygon& rPolyPoly, bool bPolyLine,
7120 const tools::Rectangle& rRect1, const tools::Rectangle& rRect2 )
7121{
7122 // TODO: pass nWidth as width of this mark
7123 // long nWidth = 0;
7124
7125 if ( rPolyPoly.Count() )
7126 {
7127 if ( bPolyLine )
7128 {
7129 tools::Polygon aPoly = rPolyPoly.GetObject( 0 );
7130 aPoly.Move( nX, nY );
7131 drawPolyLine( aPoly );
7132 }
7133 else
7134 {
7135 tools::PolyPolygon aPolyPoly = rPolyPoly;
7136 aPolyPoly.Move( nX, nY );
7137 drawPolyPolygon( aPolyPoly );
7138 }
7139 }
7140
7141 if ( !rRect1.IsEmpty() )
7142 {
7143 tools::Rectangle aRect( Point( nX+rRect1.Left(),
7144 nY+rRect1.Top() ), rRect1.GetSize() );
7145 drawRectangle( aRect );
7146 }
7147
7148 if ( !rRect2.IsEmpty() )
7149 {
7150 tools::Rectangle aRect( Point( nX+rRect2.Left(),
7151 nY+rRect2.Top() ), rRect2.GetSize() );
7152
7153 drawRectangle( aRect );
7154 }
7155}
7156
7157void PDFWriterImpl::drawText( const Point& rPos, const OUString& rText, sal_Int32 nIndex, sal_Int32 nLen, bool bTextLines )
7158{
7159 MARK( "drawText" );
7160
7162
7163 // get a layout from the OutputDevice's SalGraphics
7164 // this also enforces font substitution and sets the font on SalGraphics
7165 const SalLayoutGlyphs* layoutGlyphs = SalLayoutGlyphsCache::self()->
7166 GetLayoutGlyphs( this, rText, nIndex, nLen );
7167 std::unique_ptr<SalLayout> pLayout = ImplLayout( rText, nIndex, nLen, rPos,
7168 0, {}, {}, SalLayoutFlags::NONE, nullptr, layoutGlyphs );
7169 if( pLayout )
7170 {
7171 drawLayout( *pLayout, rText, bTextLines );
7172 }
7173}
7174
7175void PDFWriterImpl::drawTextArray( const Point& rPos, const OUString& rText, KernArraySpan pDXArray, o3tl::span<const sal_Bool> pKashidaArray, sal_Int32 nIndex, sal_Int32 nLen )
7176{
7177 MARK( "drawText with array" );
7178
7180
7181 // get a layout from the OutputDevice's SalGraphics
7182 // this also enforces font substitution and sets the font on SalGraphics
7183 const SalLayoutGlyphs* layoutGlyphs = SalLayoutGlyphsCache::self()->
7184 GetLayoutGlyphs( this, rText, nIndex, nLen );
7185 std::unique_ptr<SalLayout> pLayout = ImplLayout( rText, nIndex, nLen, rPos, 0, pDXArray, pKashidaArray,
7186 SalLayoutFlags::NONE, nullptr, layoutGlyphs );
7187 if( pLayout )
7188 {
7189 drawLayout( *pLayout, rText, true );
7190 }
7191}
7192
7193void PDFWriterImpl::drawStretchText( const Point& rPos, sal_Int32 nWidth, const OUString& rText, sal_Int32 nIndex, sal_Int32 nLen )
7194{
7195 MARK( "drawStretchText" );
7196
7198
7199 // get a layout from the OutputDevice's SalGraphics
7200 // this also enforces font substitution and sets the font on SalGraphics
7201 const SalLayoutGlyphs* layoutGlyphs = SalLayoutGlyphsCache::self()->
7202 GetLayoutGlyphs( this, rText, nIndex, nLen, nWidth );
7203 std::unique_ptr<SalLayout> pLayout = ImplLayout( rText, nIndex, nLen, rPos, nWidth,
7204 {}, {}, SalLayoutFlags::NONE, nullptr, layoutGlyphs );
7205 if( pLayout )
7206 {
7207 drawLayout( *pLayout, rText, true );
7208 }
7209}
7210
7211void PDFWriterImpl::drawText( const tools::Rectangle& rRect, const OUString& rOrigStr, DrawTextFlags nStyle )
7212{
7213 tools::Long nWidth = rRect.GetWidth();
7214 tools::Long nHeight = rRect.GetHeight();
7215
7216 if ( nWidth <= 0 || nHeight <= 0 )
7217 return;
7218
7219 MARK( "drawText with rectangle" );
7220
7222
7223 // clip with rectangle
7224 OStringBuffer aLine;
7225 aLine.append( "q " );
7226 m_aPages.back().appendRect( rRect, aLine );
7227 aLine.append( " W* n\n" );
7228 writeBuffer( aLine );
7229
7230 // if disabled text is needed, put in here
7231
7232 Point aPos = rRect.TopLeft();
7233
7234 tools::Long nTextHeight = GetTextHeight();
7235 sal_Int32 nMnemonicPos = -1;
7236
7237 OUString aStr = rOrigStr;
7238 if ( nStyle & DrawTextFlags::Mnemonic )
7239 aStr = removeMnemonicFromString( aStr, nMnemonicPos );
7240
7241 // multiline text
7242 if ( nStyle & DrawTextFlags::MultiLine )
7243 {
7244 ImplMultiTextLineInfo aMultiLineInfo;
7245 sal_Int32 i;
7246 sal_Int32 nFormatLines;
7247
7248 if ( nTextHeight )
7249 {
7250 vcl::DefaultTextLayout aLayout( *this );
7251 OUString aLastLine;
7252 OutputDevice::ImplGetTextLines( rRect, nTextHeight, aMultiLineInfo, nWidth, aStr, nStyle, aLayout );
7253 sal_Int32 nLines = nHeight/nTextHeight;
7254 nFormatLines = aMultiLineInfo.Count();
7255 if ( !nLines )
7256 nLines = 1;
7257 if ( nFormatLines > nLines )
7258 {
7259 if ( nStyle & DrawTextFlags::EndEllipsis )
7260 {
7261 // handle last line
7262 nFormatLines = nLines-1;
7263
7264 ImplTextLineInfo& rLineInfo = aMultiLineInfo.GetLine( nFormatLines );
7265 aLastLine = convertLineEnd(aStr.copy(rLineInfo.GetIndex()), LINEEND_LF);
7266 // replace line feed by space
7267 aLastLine = aLastLine.replace('\n', ' ');
7268 aLastLine = GetEllipsisString( aLastLine, nWidth, nStyle );
7270 nStyle |= DrawTextFlags::Top;
7271 }
7272 }
7273
7274 // vertical alignment
7275 if ( nStyle & DrawTextFlags::Bottom )
7276 aPos.AdjustY(nHeight-(nFormatLines*nTextHeight) );
7277 else if ( nStyle & DrawTextFlags::VCenter )
7278 aPos.AdjustY((nHeight-(nFormatLines*nTextHeight))/2 );
7279
7280 // draw all lines excluding the last
7281 for ( i = 0; i < nFormatLines; i++ )
7282 {
7283 ImplTextLineInfo& rLineInfo = aMultiLineInfo.GetLine( i );
7284 if ( nStyle & DrawTextFlags::Right )
7285 aPos.AdjustX(nWidth-rLineInfo.GetWidth() );
7286 else if ( nStyle & DrawTextFlags::Center )
7287 aPos.AdjustX((nWidth-rLineInfo.GetWidth())/2 );
7288 sal_Int32 nIndex = rLineInfo.GetIndex();
7289 sal_Int32 nLineLen = rLineInfo.GetLen();
7290 drawText( aPos, aStr, nIndex, nLineLen );
7291 // mnemonics should not appear in documents,
7292 // if the need arises, put them in here
7293 aPos.AdjustY(nTextHeight );
7294 aPos.setX( rRect.Left() );
7295 }
7296
7297 // output last line left adjusted since it was shortened
7298 if (!aLastLine.isEmpty())
7299 drawText( aPos, aLastLine, 0, aLastLine.getLength() );
7300 }
7301 }
7302 else
7303 {
7304 tools::Long nTextWidth = GetTextWidth( aStr );
7305
7306 // Evt. Text kuerzen
7307 if ( nTextWidth > nWidth )
7308 {
7310 {
7311 aStr = GetEllipsisString( aStr, nWidth, nStyle );
7313 nStyle |= DrawTextFlags::Left;
7314 nTextWidth = GetTextWidth( aStr );
7315 }
7316 }
7317
7318 // vertical alignment
7319 if ( nStyle & DrawTextFlags::Right )
7320 aPos.AdjustX(nWidth-nTextWidth );
7321 else if ( nStyle & DrawTextFlags::Center )
7322 aPos.AdjustX((nWidth-nTextWidth)/2 );
7323
7324 if ( nStyle & DrawTextFlags::Bottom )
7325 aPos.AdjustY(nHeight-nTextHeight );
7326 else if ( nStyle & DrawTextFlags::VCenter )
7327 aPos.AdjustY((nHeight-nTextHeight)/2 );
7328
7329 // mnemonics should be inserted here if the need arises
7330
7331 // draw the actual text
7332 drawText( aPos, aStr, 0, aStr.getLength() );
7333 }
7334
7335 // reset clip region to original value
7336 aLine.setLength( 0 );
7337 aLine.append( "Q\n" );
7338 writeBuffer( aLine );
7339}
7340
7341void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop )
7342{
7343 MARK( "drawLine" );
7344
7346
7347 if( m_aGraphicsStack.front().m_aLineColor == COL_TRANSPARENT )
7348 return;
7349
7350 OStringBuffer aLine;
7351 m_aPages.back().appendPoint( rStart, aLine );
7352 aLine.append( " m " );
7353 m_aPages.back().appendPoint( rStop, aLine );
7354 aLine.append( " l S\n" );
7355
7356 writeBuffer( aLine );
7357}
7358
7359void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop, const LineInfo& rInfo )
7360{
7361 MARK( "drawLine with LineInfo" );
7363
7364 if( m_aGraphicsStack.front().m_aLineColor == COL_TRANSPARENT )
7365 return;
7366
7367 if( rInfo.GetStyle() == LineStyle::Solid && rInfo.GetWidth() < 2 )
7368 {
7369 drawLine( rStart, rStop );
7370 return;
7371 }
7372
7373 OStringBuffer aLine;
7374
7375 aLine.append( "q " );
7376 if( m_aPages.back().appendLineInfo( rInfo, aLine ) )
7377 {
7378 m_aPages.back().appendPoint( rStart, aLine );
7379 aLine.append( " m " );
7380 m_aPages.back().appendPoint( rStop, aLine );
7381 aLine.append( " l S Q\n" );
7382
7383 writeBuffer( aLine );
7384 }
7385 else
7386 {
7388 convertLineInfoToExtLineInfo( rInfo, aInfo );
7389 Point aPolyPoints[2] = { rStart, rStop };
7390 tools::Polygon aPoly( 2, aPolyPoints );
7391 drawPolyLine( aPoly, aInfo );
7392 }
7393}
7394
7395#define HCONV( x ) ImplDevicePixelToLogicHeight( x )
7396
7397void PDFWriterImpl::drawWaveTextLine( OStringBuffer& aLine, tools::Long nWidth, FontLineStyle eTextLine, Color aColor, bool bIsAbove )
7398{
7399 // note: units in pFontInstance are ref device pixel
7400 const LogicalFontInstance* pFontInstance = GetFontInstance();
7401 tools::Long nLineHeight = 0;
7402 tools::Long nLinePos = 0;
7403
7404 appendStrokingColor( aColor, aLine );
7405 aLine.append( "\n" );
7406
7407 if ( bIsAbove )
7408 {
7409 if ( !pFontInstance->mxFontMetric->GetAboveWavelineUnderlineSize() )
7411 nLineHeight = HCONV( pFontInstance->mxFontMetric->GetAboveWavelineUnderlineSize() );
7412 nLinePos = HCONV( pFontInstance->mxFontMetric->GetAboveWavelineUnderlineOffset() );
7413 }
7414 else
7415 {
7416 if ( !pFontInstance->mxFontMetric->GetWavelineUnderlineSize() )
7418 nLineHeight = HCONV( pFontInstance->mxFontMetric->GetWavelineUnderlineSize() );
7419 nLinePos = HCONV( pFontInstance->mxFontMetric->GetWavelineUnderlineOffset() );
7420 }
7421 if ( (eTextLine == LINESTYLE_SMALLWAVE) && (nLineHeight > 3) )
7422 nLineHeight = 3;
7423
7425 if ( ! nLineWidth )
7426 nLineWidth = 1;
7427
7428 if ( eTextLine == LINESTYLE_BOLDWAVE )
7430
7431 m_aPages.back().appendMappedLength( static_cast<sal_Int32>(nLineWidth), aLine );
7432 aLine.append( " w " );
7433
7434 if ( eTextLine == LINESTYLE_DOUBLEWAVE )
7435 {
7436 tools::Long nOrgLineHeight = nLineHeight;
7437 nLineHeight /= 3;
7438 if ( nLineHeight < 2 )
7439 {
7440 if ( nOrgLineHeight > 1 )
7441 nLineHeight = 2;
7442 else
7443 nLineHeight = 1;
7444 }
7445 tools::Long nLineDY = nOrgLineHeight-(nLineHeight*2);
7446 if ( nLineDY < nLineWidth )
7447 nLineDY = nLineWidth;
7448 tools::Long nLineDY2 = nLineDY/2;
7449 if ( !nLineDY2 )
7450 nLineDY2 = 1;
7451
7452 nLinePos -= nLineWidth-nLineDY2;
7453
7454 m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine );
7455
7456 nLinePos += nLineWidth+nLineDY;
7457 m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine );
7458 }
7459 else
7460 {
7461 if ( eTextLine != LINESTYLE_BOLDWAVE )
7462 nLinePos -= nLineWidth/2;
7463 m_aPages.back().appendWaveLine( nWidth, -nLinePos, nLineHeight, aLine );
7464 }
7465}
7466
7467void PDFWriterImpl::drawStraightTextLine( OStringBuffer& aLine, tools::Long nWidth, FontLineStyle eTextLine, Color aColor, bool bIsAbove )
7468{
7469 // note: units in pFontInstance are ref device pixel
7470 const LogicalFontInstance* pFontInstance = GetFontInstance();
7471 tools::Long nLineHeight = 0;
7472 tools::Long nLinePos = 0;
7473 tools::Long nLinePos2 = 0;
7474
7475 if ( eTextLine > LINESTYLE_BOLDWAVE )
7476 eTextLine = LINESTYLE_SINGLE;
7477
7478 switch ( eTextLine )
7479 {
7480 case LINESTYLE_SINGLE:
7481 case LINESTYLE_DOTTED:
7482 case LINESTYLE_DASH:
7483 case LINESTYLE_LONGDASH:
7484 case LINESTYLE_DASHDOT:
7486 if ( bIsAbove )
7487 {
7488 if ( !pFontInstance->mxFontMetric->GetAboveUnderlineSize() )
7490 nLineHeight = pFontInstance->mxFontMetric->GetAboveUnderlineSize();
7491 nLinePos = pFontInstance->mxFontMetric->GetAboveUnderlineOffset();
7492 }
7493 else
7494 {
7495 if ( !pFontInstance->mxFontMetric->GetUnderlineSize() )
7497 nLineHeight = pFontInstance->mxFontMetric->GetUnderlineSize();
7498 nLinePos = pFontInstance->mxFontMetric->GetUnderlineOffset();
7499 }
7500 break;
7501 case LINESTYLE_BOLD:
7503 case LINESTYLE_BOLDDASH:
7507 if ( bIsAbove )
7508 {
7509 if ( !pFontInstance->mxFontMetric->GetAboveBoldUnderlineSize() )
7511 nLineHeight = pFontInstance->mxFontMetric->GetAboveBoldUnderlineSize();
7512 nLinePos = pFontInstance->mxFontMetric->GetAboveBoldUnderlineOffset();
7513 }
7514 else
7515 {
7516 if ( !pFontInstance->mxFontMetric->GetBoldUnderlineSize() )
7518 nLineHeight = pFontInstance->mxFontMetric->GetBoldUnderlineSize();
7519 nLinePos = pFontInstance->mxFontMetric->GetBoldUnderlineOffset();
7520 }
7521 break;
7522 case LINESTYLE_DOUBLE:
7523 if ( bIsAbove )
7524 {
7525 if ( !pFontInstance->mxFontMetric->GetAboveDoubleUnderlineSize() )
7527 nLineHeight = pFontInstance->mxFontMetric->GetAboveDoubleUnderlineSize();
7528 nLinePos = pFontInstance->mxFontMetric->GetAboveDoubleUnderlineOffset1();
7529 nLinePos2 = pFontInstance->mxFontMetric->GetAboveDoubleUnderlineOffset2();
7530 }
7531 else
7532 {
7533 if ( !pFontInstance->mxFontMetric->GetDoubleUnderlineSize() )
7535 nLineHeight = pFontInstance->mxFontMetric->GetDoubleUnderlineSize();
7536 nLinePos = pFontInstance->mxFontMetric->GetDoubleUnderlineOffset1();
7537 nLinePos2 = pFontInstance->mxFontMetric->GetDoubleUnderlineOffset2();
7538 }
7539 break;
7540 default:
7541 break;
7542 }
7543
7544 if ( !nLineHeight )
7545 return;
7546
7547 // tdf#154235
7548 // nLinePos/nLinePos2 is the distance from baseline to the top of the line,
7549 // while in PDF we stroke the line so the position is to the middle of the
7550 // line, we add half of nLineHeight to account for that.
7551 auto nOffset = nLineHeight / 2;
7553 {
7554 // Except when outlining, as now we are drawing a rectangle, so
7555 // nLinePos is the bottom of the rectangle, so need to add nLineHeight
7556 // to it.
7557 nOffset = nLineHeight;
7558 }
7559
7560 nLineHeight = HCONV(nLineHeight);
7561 nLinePos = HCONV(nLinePos + nOffset);
7562 nLinePos2 = HCONV(nLinePos2 + nOffset);
7563
7564 // outline attribute ?
7566 {
7567 appendStrokingColor(aColor, aLine); // stroke with text color
7568 aLine.append( " " );
7569 appendNonStrokingColor(COL_WHITE, aLine); // fill with white
7570 aLine.append( "\n" );
7571 aLine.append( "0.25 w \n" ); // same line thickness as in drawLayout
7572
7573 // draw rectangle instead
7574 aLine.append( "0 " );
7575 m_aPages.back().appendMappedLength( static_cast<sal_Int32>(-nLinePos), aLine );
7576 aLine.append( " " );
7577 m_aPages.back().appendMappedLength( static_cast<sal_Int32>(nWidth), aLine, false );
7578 aLine.append( ' ' );
7579 m_aPages.back().appendMappedLength( static_cast<sal_Int32>(nLineHeight), aLine );
7580 aLine.append( " re h B\n" );
7581 return;
7582 }
7583
7584
7585 m_aPages.back().appendMappedLength( static_cast<sal_Int32>(nLineHeight), aLine );
7586 aLine.append( " w " );
7587 appendStrokingColor( aColor, aLine );
7588 aLine.append( "\n" );
7589
7590 switch ( eTextLine )
7591 {
7592 case LINESTYLE_DOTTED:
7594 aLine.append( "[ " );
7595 m_aPages.back().appendMappedLength( static_cast<sal_Int32>(nLineHeight), aLine, false );
7596 aLine.append( " ] 0 d\n" );
7597 break;
7598 case LINESTYLE_DASH:
7599 case LINESTYLE_LONGDASH:
7600 case LINESTYLE_BOLDDASH:
7602 {
7603 sal_Int32 nDashLength = 4*nLineHeight;
7604 sal_Int32 nVoidLength = 2*nLineHeight;
7605 if ( ( eTextLine == LINESTYLE_LONGDASH ) || ( eTextLine == LINESTYLE_BOLDLONGDASH ) )
7606 nDashLength = 8*nLineHeight;
7607
7608 aLine.append( "[ " );
7609 m_aPages.back().appendMappedLength( nDashLength, aLine, false );
7610 aLine.append( ' ' );
7611 m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
7612 aLine.append( " ] 0 d\n" );
7613 }
7614 break;
7615 case LINESTYLE_DASHDOT:
7617 {
7618 sal_Int32 nDashLength = 4*nLineHeight;
7619 sal_Int32 nVoidLength = 2*nLineHeight;
7620 aLine.append( "[ " );
7621 m_aPages.back().appendMappedLength( nDashLength, aLine, false );
7622 aLine.append( ' ' );
7623 m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
7624 aLine.append( ' ' );
7625 m_aPages.back().appendMappedLength( static_cast<sal_Int32>(nLineHeight), aLine, false );
7626 aLine.append( ' ' );
7627 m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
7628 aLine.append( " ] 0 d\n" );
7629 }
7630 break;
7633 {
7634 sal_Int32 nDashLength = 4*nLineHeight;
7635 sal_Int32 nVoidLength = 2*nLineHeight;
7636 aLine.append( "[ " );
7637 m_aPages.back().appendMappedLength( nDashLength, aLine, false );
7638 aLine.append( ' ' );
7639 m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
7640 aLine.append( ' ' );
7641 m_aPages.back().appendMappedLength( static_cast<sal_Int32>(nLineHeight), aLine, false );
7642 aLine.append( ' ' );
7643 m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
7644 aLine.append( ' ' );
7645 m_aPages.back().appendMappedLength( static_cast<sal_Int32>(nLineHeight), aLine, false );
7646 aLine.append( ' ' );
7647 m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
7648 aLine.append( " ] 0 d\n" );
7649 }
7650 break;
7651 default:
7652 break;
7653 }
7654
7655 aLine.append( "0 " );
7656 m_aPages.back().appendMappedLength( static_cast<sal_Int32>(-nLinePos), aLine );
7657 aLine.append( " m " );
7658 m_aPages.back().appendMappedLength( static_cast<sal_Int32>(nWidth), aLine, false );
7659 aLine.append( ' ' );
7660 m_aPages.back().appendMappedLength( static_cast<sal_Int32>(-nLinePos), aLine );
7661 aLine.append( " l S\n" );
7662 if ( eTextLine == LINESTYLE_DOUBLE )
7663 {
7664 aLine.append( "0 " );
7665 m_aPages.back().appendMappedLength( static_cast<sal_Int32>(-nLinePos2-nLineHeight), aLine );
7666 aLine.append( " m " );
7667 m_aPages.back().appendMappedLength( static_cast<sal_Int32>(nWidth), aLine, false );
7668 aLine.append( ' ' );
7669 m_aPages.back().appendMappedLength( static_cast<sal_Int32>(-nLinePos2-nLineHeight), aLine );
7670 aLine.append( " l S\n" );
7671 }
7672
7673}
7674
7675void PDFWriterImpl::drawStrikeoutLine( OStringBuffer& aLine, tools::Long nWidth, FontStrikeout eStrikeout, Color aColor )
7676{
7677 // note: units in pFontInstance are ref device pixel
7678 const LogicalFontInstance* pFontInstance = GetFontInstance();
7679 tools::Long nLineHeight = 0;
7680 tools::Long nLinePos = 0;
7681 tools::Long nLinePos2 = 0;
7682
7683 if ( eStrikeout > STRIKEOUT_X )
7684 eStrikeout = STRIKEOUT_SINGLE;
7685
7686 switch ( eStrikeout )
7687 {
7688 case STRIKEOUT_SINGLE:
7689 if ( !pFontInstance->mxFontMetric->GetStrikeoutSize() )
7691 nLineHeight = pFontInstance->mxFontMetric->GetStrikeoutSize();
7692 nLinePos = pFontInstance->mxFontMetric->GetStrikeoutOffset();
7693 break;
7694 case STRIKEOUT_BOLD:
7695 if ( !pFontInstance->mxFontMetric->GetBoldStrikeoutSize() )
7697 nLineHeight = pFontInstance->mxFontMetric->GetBoldStrikeoutSize();
7698 nLinePos = pFontInstance->mxFontMetric->GetBoldStrikeoutOffset();
7699 break;
7700 case STRIKEOUT_DOUBLE:
7701 if ( !pFontInstance->mxFontMetric->GetDoubleStrikeoutSize() )
7703 nLineHeight = pFontInstance->mxFontMetric->GetDoubleStrikeoutSize();
7704 nLinePos = pFontInstance->mxFontMetric->GetDoubleStrikeoutOffset1();
7705 nLinePos2 = pFontInstance->mxFontMetric->GetDoubleStrikeoutOffset2();
7706 break;
7707 default:
7708 break;
7709 }
7710
7711 if ( !nLineHeight )
7712 return;
7713
7714 // tdf#154235
7715 // nLinePos/nLinePos2 is the distance from baseline to the bottom of the line,
7716 // while in PDF we stroke the line so the position is to the middle of the
7717 // line, we add half of nLineHeight to account for that.
7718 nLinePos = HCONV(nLinePos + nLineHeight / 2);
7719 nLinePos2 = HCONV(nLinePos2 + nLineHeight / 2);
7720 nLineHeight = HCONV(nLineHeight);
7721
7722 m_aPages.back().appendMappedLength( static_cast<sal_Int32>(nLineHeight), aLine );
7723 aLine.append( " w " );
7724 appendStrokingColor( aColor, aLine );
7725 aLine.append( "\n" );
7726
7727 aLine.append( "0 " );
7728 m_aPages.back().appendMappedLength( static_cast<sal_Int32>(-nLinePos), aLine );
7729 aLine.append( " m " );
7730 m_aPages.back().appendMappedLength( static_cast<sal_Int32>(nWidth), aLine );
7731 aLine.append( ' ' );
7732 m_aPages.back().appendMappedLength( static_cast<sal_Int32>(-nLinePos), aLine );
7733 aLine.append( " l S\n" );
7734
7735 if ( eStrikeout == STRIKEOUT_DOUBLE )
7736 {
7737 aLine.append( "0 " );
7738 m_aPages.back().appendMappedLength( static_cast<sal_Int32>(-nLinePos2-nLineHeight), aLine );
7739 aLine.append( " m " );
7740 m_aPages.back().appendMappedLength( static_cast<sal_Int32>(nWidth), aLine );
7741 aLine.append( ' ' );
7742 m_aPages.back().appendMappedLength( static_cast<sal_Int32>(-nLinePos2-nLineHeight), aLine );
7743 aLine.append( " l S\n" );
7744 }
7745
7746}
7747
7749{
7750 //See qadevOOo/testdocs/StrikeThrough.odt for examples if you need
7751 //to tweak this
7752
7753 OUString aStrikeoutChar = eStrikeout == STRIKEOUT_SLASH ? OUString( "/" ) : OUString( "X" );
7754 OUString aStrikeout = aStrikeoutChar;
7755 while( GetTextWidth( aStrikeout ) < nWidth )
7756 aStrikeout += aStrikeout;
7757
7758 // do not get broader than nWidth modulo 1 character
7759 while( GetTextWidth( aStrikeout ) >= nWidth )
7760 aStrikeout = aStrikeout.copy(1);
7761 aStrikeout += aStrikeoutChar;
7762 bool bShadow = m_aCurrentPDFState.m_aFont.IsShadow();
7763 if ( bShadow )
7764 {
7766 aFont.SetShadow( false );
7767 setFont( aFont );
7769 }
7770
7771 // strikeout string is left aligned non-CTL text
7774
7776 FontMetric aRefDevFontMetric = GetFontMetric();
7777 tools::Rectangle aRect;
7778 aRect.SetLeft( rPos.X() );
7779 aRect.SetRight( aRect.Left()+nWidth );
7780 aRect.SetBottom( rPos.Y()+aRefDevFontMetric.GetDescent() );
7781 aRect.SetTop( rPos.Y()-aRefDevFontMetric.GetAscent() );
7782
7783 const LogicalFontInstance* pFontInstance = GetFontInstance();
7784 if (pFontInstance->mnOrientation)
7785 {
7786 tools::Polygon aPoly( aRect );
7787 aPoly.Rotate( rPos, pFontInstance->mnOrientation);
7788 aRect = aPoly.GetBoundRect();
7789 }
7790
7791 intersectClipRegion( aRect );
7792 drawText( rPos, aStrikeout, 0, aStrikeout.getLength(), false );
7793 pop();
7794
7795 SetLayoutMode( nOrigTLM );
7796
7797 if ( bShadow )
7798 {
7800 aFont.SetShadow( true );
7801 setFont( aFont );
7803 }
7804}
7805
7806void PDFWriterImpl::drawTextLine( const Point& rPos, tools::Long nWidth, FontStrikeout eStrikeout, FontLineStyle eUnderline, FontLineStyle eOverline, bool bUnderlineAbove )
7807{
7808 if ( !nWidth ||
7809 ( ((eStrikeout == STRIKEOUT_NONE)||(eStrikeout == STRIKEOUT_DONTKNOW)) &&
7810 ((eUnderline == LINESTYLE_NONE)||(eUnderline == LINESTYLE_DONTKNOW)) &&
7811 ((eOverline == LINESTYLE_NONE)||(eOverline == LINESTYLE_DONTKNOW)) ) )
7812 return;
7813
7814 MARK( "drawTextLine" );
7816
7817 // note: units in pFontInstance are ref device pixel
7818 const LogicalFontInstance* pFontInstance = GetFontInstance();
7819 Color aUnderlineColor = m_aCurrentPDFState.m_aTextLineColor;
7820 Color aOverlineColor = m_aCurrentPDFState.m_aOverlineColor;
7821 Color aStrikeoutColor = m_aCurrentPDFState.m_aFont.GetColor();
7822 bool bStrikeoutDone = false;
7823 bool bUnderlineDone = false;
7824 bool bOverlineDone = false;
7825
7826 if ( (eStrikeout == STRIKEOUT_SLASH) || (eStrikeout == STRIKEOUT_X) )
7827 {
7828 drawStrikeoutChar( rPos, nWidth, eStrikeout );
7829 bStrikeoutDone = true;
7830 }
7831
7832 Point aPos( rPos );
7834 if( eAlign == ALIGN_TOP )
7835 aPos.AdjustY(HCONV( pFontInstance->mxFontMetric->GetAscent() ));
7836 else if( eAlign == ALIGN_BOTTOM )
7837 aPos.AdjustY( -HCONV( pFontInstance->mxFontMetric->GetDescent() ) );
7838
7839 OStringBuffer aLine( 512 );
7840 // save GS
7841 aLine.append( "q " );
7842
7843 // rotate and translate matrix
7845 Matrix3 aMat;
7846 aMat.rotate( fAngle );
7847 aMat.translate( aPos.X(), aPos.Y() );
7848 m_aPages.back().appendMatrix3(aMat, aLine);
7849 aLine.append( " cm\n" );
7850
7851 if ( aUnderlineColor.IsTransparent() )
7852 aUnderlineColor = aStrikeoutColor;
7853
7854 if ( aOverlineColor.IsTransparent() )
7855 aOverlineColor = aStrikeoutColor;
7856
7857 if ( (eUnderline == LINESTYLE_SMALLWAVE) ||
7858 (eUnderline == LINESTYLE_WAVE) ||
7859 (eUnderline == LINESTYLE_DOUBLEWAVE) ||
7860 (eUnderline == LINESTYLE_BOLDWAVE) )
7861 {
7862 drawWaveTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
7863 bUnderlineDone = true;
7864 }
7865
7866 if ( (eOverline == LINESTYLE_SMALLWAVE) ||
7867 (eOverline == LINESTYLE_WAVE) ||
7868 (eOverline == LINESTYLE_DOUBLEWAVE) ||
7869 (eOverline == LINESTYLE_BOLDWAVE) )
7870 {
7871 drawWaveTextLine( aLine, nWidth, eOverline, aOverlineColor, true );
7872 bOverlineDone = true;
7873 }
7874
7875 if ( !bUnderlineDone )
7876 {
7877 drawStraightTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
7878 }
7879
7880 if ( !bOverlineDone )
7881 {
7882 drawStraightTextLine( aLine, nWidth, eOverline, aOverlineColor, true );
7883 }
7884
7885 if ( !bStrikeoutDone )
7886 {
7887 drawStrikeoutLine( aLine, nWidth, eStrikeout, aStrikeoutColor );
7888 }
7889
7890 aLine.append( "Q\n" );
7891 writeBuffer( aLine );
7892}
7893
7895{
7896 MARK( "drawPolygon" );
7897
7899
7900 if( m_aGraphicsStack.front().m_aLineColor == COL_TRANSPARENT &&
7901 m_aGraphicsStack.front().m_aFillColor == COL_TRANSPARENT )
7902 return;
7903
7904 int nPoints = rPoly.GetSize();
7905 OStringBuffer aLine( 20 * nPoints );
7906 m_aPages.back().appendPolygon( rPoly, aLine );
7907 if( m_aGraphicsStack.front().m_aLineColor != COL_TRANSPARENT &&
7908 m_aGraphicsStack.front().m_aFillColor != COL_TRANSPARENT )
7909 aLine.append( "B*\n" );
7910 else if( m_aGraphicsStack.front().m_aLineColor != COL_TRANSPARENT )
7911 aLine.append( "S\n" );
7912 else
7913 aLine.append( "f*\n" );
7914
7915 writeBuffer( aLine );
7916}
7917
7919{
7920 MARK( "drawPolyPolygon" );
7921
7923
7924 if( m_aGraphicsStack.front().m_aLineColor == COL_TRANSPARENT &&
7925 m_aGraphicsStack.front().m_aFillColor == COL_TRANSPARENT )
7926 return;
7927
7928 int nPolygons = rPolyPoly.Count();
7929
7930 OStringBuffer aLine( 40 * nPolygons );
7931 m_aPages.back().appendPolyPolygon( rPolyPoly, aLine );
7932 if( m_aGraphicsStack.front().m_aLineColor != COL_TRANSPARENT &&
7933 m_aGraphicsStack.front().m_aFillColor != COL_TRANSPARENT )
7934 aLine.append( "B*\n" );
7935 else if( m_aGraphicsStack.front().m_aLineColor != COL_TRANSPARENT )
7936 aLine.append( "S\n" );
7937 else
7938 aLine.append( "f*\n" );
7939
7940 writeBuffer( aLine );
7941}
7942
7943void PDFWriterImpl::drawTransparent( const tools::PolyPolygon& rPolyPoly, sal_uInt32 nTransparentPercent )
7944{
7945 SAL_WARN_IF( nTransparentPercent > 100, "vcl.pdfwriter", "invalid alpha value" );
7946 nTransparentPercent = nTransparentPercent % 100;
7947
7948 MARK( "drawTransparent" );
7949
7951
7952 if( m_aGraphicsStack.front().m_aLineColor == COL_TRANSPARENT &&
7953 m_aGraphicsStack.front().m_aFillColor == COL_TRANSPARENT )
7954 return;
7955
7957 {
7958 m_aErrors.insert( m_bIsPDF_A1 ?
7961
7962 drawPolyPolygon( rPolyPoly );
7963 return;
7964 }
7965
7966 // create XObject
7967 m_aTransparentObjects.emplace_back( );
7968 // FIXME: polygons with beziers may yield incorrect bound rect
7969 m_aTransparentObjects.back().m_aBoundRect = rPolyPoly.GetBoundRect();
7970 // convert rectangle to default user space
7971 m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect );
7972 m_aTransparentObjects.back().m_nObject = createObject();
7973 m_aTransparentObjects.back().m_nExtGStateObject = createObject();
7974 m_aTransparentObjects.back().m_fAlpha = static_cast<double>(100-nTransparentPercent) / 100.0;
7975 m_aTransparentObjects.back().m_pContentStream.reset(new SvMemoryStream( 256, 256 ));
7976 // create XObject's content stream
7977 OStringBuffer aContent( 256 );
7978 m_aPages.back().appendPolyPolygon( rPolyPoly, aContent );
7981 aContent.append( " B*\n" );
7983 aContent.append( " S\n" );
7984 else
7985 aContent.append( " f*\n" );
7986 m_aTransparentObjects.back().m_pContentStream->WriteBytes(
7987 aContent.getStr(), aContent.getLength() );
7988
7989 OStringBuffer aObjName( 16 );
7990 aObjName.append( "Tr" );
7991 aObjName.append( m_aTransparentObjects.back().m_nObject );
7992 OString aTrName( aObjName.makeStringAndClear() );
7993 aObjName.append( "EGS" );
7994 aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject );
7995 OString aExtName( aObjName.makeStringAndClear() );
7996
7997 OString aLine =
7998 // insert XObject
7999 "q /" +
8000 aExtName +
8001 " gs /" +
8002 aTrName +
8003 " Do Q\n";
8004 writeBuffer( aLine );
8005
8006 pushResource( ResourceKind::XObject, aTrName, m_aTransparentObjects.back().m_nObject );
8007 pushResource( ResourceKind::ExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject );
8008}
8009
8010void PDFWriterImpl::pushResource(ResourceKind eKind, const OString& rResource, sal_Int32 nObject, ResourceDict& rResourceDict, std::list<StreamRedirect>& rOutputStreams)
8011{
8012 if( nObject < 0 )
8013 return;
8014
8015 switch( eKind )
8016 {
8017 case ResourceKind::XObject:
8018 rResourceDict.m_aXObjects[rResource] = nObject;
8019 if (!rOutputStreams.empty())
8020 rOutputStreams.front().m_aResourceDict.m_aXObjects[rResource] = nObject;
8021 break;
8022 case ResourceKind::ExtGState:
8023 rResourceDict.m_aExtGStates[rResource] = nObject;
8024 if (!rOutputStreams.empty())
8025 rOutputStreams.front().m_aResourceDict.m_aExtGStates[rResource] = nObject;
8026 break;
8027 case ResourceKind::Shading:
8028 rResourceDict.m_aShadings[rResource] = nObject;
8029 if (!rOutputStreams.empty())
8030 rOutputStreams.front().m_aResourceDict.m_aShadings[rResource] = nObject;
8031 break;
8033 rResourceDict.m_aPatterns[rResource] = nObject;
8034 if (!rOutputStreams.empty())
8035 rOutputStreams.front().m_aResourceDict.m_aPatterns[rResource] = nObject;
8036 break;
8037 }
8038}
8039
8040void PDFWriterImpl::pushResource( ResourceKind eKind, const OString& rResource, sal_Int32 nObject )
8041{
8042 pushResource(eKind, rResource, nObject, m_aGlobalResourceDict, m_aOutputStreams);
8043}
8044
8045void PDFWriterImpl::beginRedirect( SvStream* pStream, const tools::Rectangle& rTargetRect )
8046{
8048
8049 // force reemitting clip region inside the new stream, and
8050 // prevent emitting an unbalanced "Q" at the start
8052 // this is needed to point m_aCurrentPDFState at the pushed state
8053 // ... but it's pointless to actually write into the "outer" stream here!
8054 updateGraphicsState(Mode::NOWRITE);
8055
8056 m_aOutputStreams.push_front( StreamRedirect() );
8057 m_aOutputStreams.front().m_pStream = pStream;
8058 m_aOutputStreams.front().m_aMapMode = m_aMapMode;
8059
8060 if( !rTargetRect.IsEmpty() )
8061 {
8062 m_aOutputStreams.front().m_aTargetRect =
8063 lcl_convert( m_aGraphicsStack.front().m_aMapMode,
8064 m_aMapMode,
8065 this,
8066 rTargetRect );
8067 Point aDelta = m_aOutputStreams.front().m_aTargetRect.BottomLeft();
8068 tools::Long nPageHeight = pointToPixel(m_aPages[m_nCurrentPage].getHeight());
8069 aDelta.setY( -(nPageHeight - m_aOutputStreams.front().m_aTargetRect.Bottom()) );
8071 }
8072
8073 // setup graphics state for independent object stream
8074
8075 // force reemitting colors
8078}
8079
8081{
8082 SvStream* pStream = nullptr;
8083 if( ! m_aOutputStreams.empty() )
8084 {
8085 pStream = m_aOutputStreams.front().m_pStream;
8086 m_aMapMode = m_aOutputStreams.front().m_aMapMode;
8087 m_aOutputStreams.pop_front();
8088 }
8089
8090 pop();
8091
8094
8095 // needed after pop() to set m_aCurrentPDFState
8096 updateGraphicsState(Mode::NOWRITE);
8097
8098 return pStream;
8099}
8100
8102{
8105 beginRedirect( new SvMemoryStream( 1024, 1024 ), tools::Rectangle() );
8106}
8107
8108void PDFWriterImpl::endTransparencyGroup( const tools::Rectangle& rBoundingBox, sal_uInt32 nTransparentPercent )
8109{
8110 SAL_WARN_IF( nTransparentPercent > 100, "vcl.pdfwriter", "invalid alpha value" );
8111 nTransparentPercent = nTransparentPercent % 100;
8112
8114 return;
8115
8116 // create XObject
8117 m_aTransparentObjects.emplace_back( );
8118 m_aTransparentObjects.back().m_aBoundRect = rBoundingBox;
8119 // convert rectangle to default user space
8120 m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect );
8121 m_aTransparentObjects.back().m_nObject = createObject();
8122 m_aTransparentObjects.back().m_fAlpha = static_cast<double>(100-nTransparentPercent) / 100.0;
8123 // get XObject's content stream
8124 m_aTransparentObjects.back().m_pContentStream.reset( static_cast<SvMemoryStream*>(endRedirect()) );
8125 m_aTransparentObjects.back().m_nExtGStateObject = createObject();
8126
8127 OStringBuffer aObjName( 16 );
8128 aObjName.append( "Tr" );
8129 aObjName.append( m_aTransparentObjects.back().m_nObject );
8130 OString aTrName( aObjName.makeStringAndClear() );
8131 aObjName.append( "EGS" );
8132 aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject );
8133 OString aExtName( aObjName.makeStringAndClear() );
8134
8135 OString aLine =
8136 // insert XObject
8137 "q /" +
8138 aExtName +
8139 " gs /" +
8140 aTrName +
8141 " Do Q\n";
8142 writeBuffer( aLine );
8143
8144 pushResource( ResourceKind::XObject, aTrName, m_aTransparentObjects.back().m_nObject );
8145 pushResource( ResourceKind::ExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject );
8146
8147}
8148
8150{
8151 MARK( "drawRectangle" );
8152
8154
8155 if( m_aGraphicsStack.front().m_aLineColor == COL_TRANSPARENT &&
8156 m_aGraphicsStack.front().m_aFillColor == COL_TRANSPARENT )
8157 return;
8158
8159 OStringBuffer aLine( 40 );
8160 m_aPages.back().appendRect( rRect, aLine );
8161
8162 if( m_aGraphicsStack.front().m_aLineColor != COL_TRANSPARENT &&
8163 m_aGraphicsStack.front().m_aFillColor != COL_TRANSPARENT )
8164 aLine.append( " B*\n" );
8165 else if( m_aGraphicsStack.front().m_aLineColor != COL_TRANSPARENT )
8166 aLine.append( " S\n" );
8167 else
8168 aLine.append( " f*\n" );
8169
8170 writeBuffer( aLine );
8171}
8172
8173void PDFWriterImpl::drawRectangle( const tools::Rectangle& rRect, sal_uInt32 nHorzRound, sal_uInt32 nVertRound )
8174{
8175 MARK( "drawRectangle with rounded edges" );
8176
8177 if( !nHorzRound && !nVertRound )
8178 drawRectangle( rRect );
8179
8181
8182 if( m_aGraphicsStack.front().m_aLineColor == COL_TRANSPARENT &&
8183 m_aGraphicsStack.front().m_aFillColor == COL_TRANSPARENT )
8184 return;
8185
8186 if( nHorzRound > static_cast<sal_uInt32>(rRect.GetWidth())/2 )
8187 nHorzRound = rRect.GetWidth()/2;
8188 if( nVertRound > static_cast<sal_uInt32>(rRect.GetHeight())/2 )
8189 nVertRound = rRect.GetHeight()/2;
8190
8191 Point aPoints[16];
8192 const double kappa = 0.5522847498;
8193 const sal_uInt32 kx = static_cast<sal_uInt32>((kappa*static_cast<double>(nHorzRound))+0.5);
8194 const sal_uInt32 ky = static_cast<sal_uInt32>((kappa*static_cast<double>(nVertRound))+0.5);
8195
8196 aPoints[1] = Point( rRect.Left() + nHorzRound, rRect.Top() );
8197 aPoints[0] = Point( aPoints[1].X() - kx, aPoints[1].Y() );
8198 aPoints[2] = Point( rRect.Right()+1 - nHorzRound, aPoints[1].Y() );
8199 aPoints[3] = Point( aPoints[2].X()+kx, aPoints[2].Y() );
8200
8201 aPoints[5] = Point( rRect.Right()+1, rRect.Top()+nVertRound );
8202 aPoints[4] = Point( aPoints[5].X(), aPoints[5].Y()-ky );
8203 aPoints[6] = Point( aPoints[5].X(), rRect.Bottom()+1 - nVertRound );
8204 aPoints[7] = Point( aPoints[6].X(), aPoints[6].Y()+ky );
8205
8206 aPoints[9] = Point( rRect.Right()+1-nHorzRound, rRect.Bottom()+1 );
8207 aPoints[8] = Point( aPoints[9].X()+kx, aPoints[9].Y() );
8208 aPoints[10] = Point( rRect.Left() + nHorzRound, aPoints[9].Y() );
8209 aPoints[11] = Point( aPoints[10].X()-kx, aPoints[10].Y() );
8210
8211 aPoints[13] = Point( rRect.Left(), rRect.Bottom()+1-nVertRound );
8212 aPoints[12] = Point( aPoints[13].X(), aPoints[13].Y()+ky );
8213 aPoints[14] = Point( rRect.Left(), rRect.Top()+nVertRound );
8214 aPoints[15] = Point( aPoints[14].X(), aPoints[14].Y()-ky );
8215
8216 OStringBuffer aLine( 80 );
8217 m_aPages.back().appendPoint( aPoints[1], aLine );
8218 aLine.append( " m " );
8219 m_aPages.back().appendPoint( aPoints[2], aLine );
8220 aLine.append( " l " );
8221 m_aPages.back().appendPoint( aPoints[3], aLine );
8222 aLine.append( ' ' );
8223 m_aPages.back().appendPoint( aPoints[4], aLine );
8224 aLine.append( ' ' );
8225 m_aPages.back().appendPoint( aPoints[5], aLine );
8226 aLine.append( " c\n" );
8227 m_aPages.back().appendPoint( aPoints[6], aLine );
8228 aLine.append( " l " );
8229 m_aPages.back().appendPoint( aPoints[7], aLine );
8230 aLine.append( ' ' );
8231 m_aPages.back().appendPoint( aPoints[8], aLine );
8232 aLine.append( ' ' );
8233 m_aPages.back().appendPoint( aPoints[9], aLine );
8234 aLine.append( " c\n" );
8235 m_aPages.back().appendPoint( aPoints[10], aLine );
8236 aLine.append( " l " );
8237 m_aPages.back().appendPoint( aPoints[11], aLine );
8238 aLine.append( ' ' );
8239 m_aPages.back().appendPoint( aPoints[12], aLine );
8240 aLine.append( ' ' );
8241 m_aPages.back().appendPoint( aPoints[13], aLine );
8242 aLine.append( " c\n" );
8243 m_aPages.back().appendPoint( aPoints[14], aLine );
8244 aLine.append( " l " );
8245 m_aPages.back().appendPoint( aPoints[15], aLine );
8246 aLine.append( ' ' );
8247 m_aPages.back().appendPoint( aPoints[0], aLine );
8248 aLine.append( ' ' );
8249 m_aPages.back().appendPoint( aPoints[1], aLine );
8250 aLine.append( " c " );
8251
8252 if( m_aGraphicsStack.front().m_aLineColor != COL_TRANSPARENT &&
8253 m_aGraphicsStack.front().m_aFillColor != COL_TRANSPARENT )
8254 aLine.append( "b*\n" );
8255 else if( m_aGraphicsStack.front().m_aLineColor != COL_TRANSPARENT )
8256 aLine.append( "s\n" );
8257 else
8258 aLine.append( "f*\n" );
8259
8260 writeBuffer( aLine );
8261}
8262
8264{
8265 MARK( "drawEllipse" );
8266
8268
8269 if( m_aGraphicsStack.front().m_aLineColor == COL_TRANSPARENT &&
8270 m_aGraphicsStack.front().m_aFillColor == COL_TRANSPARENT )
8271 return;
8272
8273 Point aPoints[12];
8274 const double kappa = 0.5522847498;
8275 const sal_uInt32 kx = static_cast<sal_uInt32>((kappa*static_cast<double>(rRect.GetWidth())/2.0)+0.5);
8276 const sal_uInt32 ky = static_cast<sal_uInt32>((kappa*static_cast<double>(rRect.GetHeight())/2.0)+0.5);
8277
8278 aPoints[1] = Point( rRect.Left() + rRect.GetWidth()/2, rRect.Top() );
8279 aPoints[0] = Point( aPoints[1].X() - kx, aPoints[1].Y() );
8280 aPoints[2] = Point( aPoints[1].X() + kx, aPoints[1].Y() );
8281
8282 aPoints[4] = Point( rRect.Right()+1, rRect.Top() + rRect.GetHeight()/2 );
8283 aPoints[3] = Point( aPoints[4].X(), aPoints[4].Y() - ky );
8284 aPoints[5] = Point( aPoints[4].X(), aPoints[4].Y() + ky );
8285
8286 aPoints[7] = Point( rRect.Left() + rRect.GetWidth()/2, rRect.Bottom()+1 );
8287 aPoints[6] = Point( aPoints[7].X() + kx, aPoints[7].Y() );
8288 aPoints[8] = Point( aPoints[7].X() - kx, aPoints[7].Y() );
8289
8290 aPoints[10] = Point( rRect.Left(), rRect.Top() + rRect.GetHeight()/2 );
8291 aPoints[9] = Point( aPoints[10].X(), aPoints[10].Y() + ky );
8292 aPoints[11] = Point( aPoints[10].X(), aPoints[10].Y() - ky );
8293
8294 OStringBuffer aLine( 80 );
8295 m_aPages.back().appendPoint( aPoints[1], aLine );
8296 aLine.append( " m " );
8297 m_aPages.back().appendPoint( aPoints[2], aLine );
8298 aLine.append( ' ' );
8299 m_aPages.back().appendPoint( aPoints[3], aLine );
8300 aLine.append( ' ' );
8301 m_aPages.back().appendPoint( aPoints[4], aLine );
8302 aLine.append( " c\n" );
8303 m_aPages.back().appendPoint( aPoints[5], aLine );
8304 aLine.append( ' ' );
8305 m_aPages.back().appendPoint( aPoints[6], aLine );
8306 aLine.append( ' ' );
8307 m_aPages.back().appendPoint( aPoints[7], aLine );
8308 aLine.append( " c\n" );
8309 m_aPages.back().appendPoint( aPoints[8], aLine );
8310 aLine.append( ' ' );
8311 m_aPages.back().appendPoint( aPoints[9], aLine );
8312 aLine.append( ' ' );
8313 m_aPages.back().appendPoint( aPoints[10], aLine );
8314 aLine.append( " c\n" );
8315 m_aPages.back().appendPoint( aPoints[11], aLine );
8316 aLine.append( ' ' );
8317 m_aPages.back().appendPoint( aPoints[0], aLine );
8318 aLine.append( ' ' );
8319 m_aPages.back().appendPoint( aPoints[1], aLine );
8320 aLine.append( " c " );
8321
8322 if( m_aGraphicsStack.front().m_aLineColor != COL_TRANSPARENT &&
8323 m_aGraphicsStack.front().m_aFillColor != COL_TRANSPARENT )
8324 aLine.append( "b*\n" );
8325 else if( m_aGraphicsStack.front().m_aLineColor != COL_TRANSPARENT )
8326 aLine.append( "s\n" );
8327 else
8328 aLine.append( "f*\n" );
8329
8330 writeBuffer( aLine );
8331}
8332
8333static double calcAngle( const tools::Rectangle& rRect, const Point& rPoint )
8334{
8335 Point aOrigin((rRect.Left()+rRect.Right()+1)/2,
8336 (rRect.Top()+rRect.Bottom()+1)/2);
8337 Point aPoint = rPoint - aOrigin;
8338
8339 double fX = static_cast<double>(aPoint.X());
8340 double fY = static_cast<double>(-aPoint.Y());
8341
8342 if ((rRect.GetHeight() == 0) || (rRect.GetWidth() == 0))
8343 throw o3tl::divide_by_zero();
8344
8345 if( rRect.GetWidth() > rRect.GetHeight() )
8346 fY = fY*(static_cast<double>(rRect.GetWidth())/static_cast<double>(rRect.GetHeight()));
8347 else if( rRect.GetHeight() > rRect.GetWidth() )
8348 fX = fX*(static_cast<double>(rRect.GetHeight())/static_cast<double>(rRect.GetWidth()));
8349 return atan2( fY, fX );
8350}
8351
8352void PDFWriterImpl::drawArc( const tools::Rectangle& rRect, const Point& rStart, const Point& rStop, bool bWithPie, bool bWithChord )
8353{
8354 MARK( "drawArc" );
8355
8357
8358 if( m_aGraphicsStack.front().m_aLineColor == COL_TRANSPARENT &&
8359 m_aGraphicsStack.front().m_aFillColor == COL_TRANSPARENT )
8360 return;
8361
8362 // calculate start and stop angles
8363 const double fStartAngle = calcAngle( rRect, rStart );
8364 double fStopAngle = calcAngle( rRect, rStop );
8365 while( fStopAngle < fStartAngle )
8366 fStopAngle += 2.0*M_PI;
8367 const int nFragments = static_cast<int>((fStopAngle-fStartAngle)/(M_PI/2.0))+1;
8368 const double fFragmentDelta = (fStopAngle-fStartAngle)/static_cast<double>(nFragments);
8369 const double kappa = fabs( 4.0 * (1.0-cos(fFragmentDelta/2.0))/sin(fFragmentDelta/2.0) / 3.0);
8370 const double halfWidth = static_cast<double>(rRect.GetWidth())/2.0;
8371 const double halfHeight = static_cast<double>(rRect.GetHeight())/2.0;
8372
8373 const Point aCenter( (rRect.Left()+rRect.Right()+1)/2,
8374 (rRect.Top()+rRect.Bottom()+1)/2 );
8375
8376 OStringBuffer aLine( 30*nFragments );
8377 Point aPoint( static_cast<int>(halfWidth * cos(fStartAngle) ),
8378 -static_cast<int>(halfHeight * sin(fStartAngle) ) );
8379 aPoint += aCenter;
8380 m_aPages.back().appendPoint( aPoint, aLine );
8381 aLine.append( " m " );
8382 if( !basegfx::fTools::equal(fStartAngle, fStopAngle) )
8383 {
8384 for( int i = 0; i < nFragments; i++ )
8385 {
8386 const double fStartFragment = fStartAngle + static_cast<double>(i)*fFragmentDelta;
8387 const double fStopFragment = fStartFragment + fFragmentDelta;
8388 aPoint = Point( static_cast<int>(halfWidth * (cos(fStartFragment) - kappa*sin(fStartFragment) ) ),
8389 -static_cast<int>(halfHeight * (sin(fStartFragment) + kappa*cos(fStartFragment) ) ) );
8390 aPoint += aCenter;
8391 m_aPages.back().appendPoint( aPoint, aLine );
8392 aLine.append( ' ' );
8393
8394 aPoint = Point( static_cast<int>(halfWidth * (cos(fStopFragment) + kappa*sin(fStopFragment) ) ),
8395 -static_cast<int>(halfHeight * (sin(fStopFragment) - kappa*cos(fStopFragment) ) ) );
8396 aPoint += aCenter;
8397 m_aPages.back().appendPoint( aPoint, aLine );
8398 aLine.append( ' ' );
8399
8400 aPoint = Point( static_cast<int>(halfWidth * cos(fStopFragment) ),
8401 -static_cast<int>(halfHeight * sin(fStopFragment) ) );
8402 aPoint += aCenter;
8403 m_aPages.back().appendPoint( aPoint, aLine );
8404 aLine.append( " c\n" );
8405 }
8406 }
8407 if( bWithChord || bWithPie )
8408 {
8409 if( bWithPie )
8410 {
8411 m_aPages.back().appendPoint( aCenter, aLine );
8412 aLine.append( " l " );
8413 }
8414 aLine.append( "h " );
8415 }
8416 if( ! bWithChord && ! bWithPie )
8417 aLine.append( "S\n" );
8418 else if( m_aGraphicsStack.front().m_aLineColor != COL_TRANSPARENT &&
8419 m_aGraphicsStack.front().m_aFillColor != COL_TRANSPARENT )
8420 aLine.append( "B*\n" );
8421 else if( m_aGraphicsStack.front().m_aLineColor != COL_TRANSPARENT )
8422 aLine.append( "S\n" );
8423 else
8424 aLine.append( "f*\n" );
8425
8426 writeBuffer( aLine );
8427}
8428
8430{
8431 MARK( "drawPolyLine" );
8432
8433 sal_uInt16 nPoints = rPoly.GetSize();
8434 if( nPoints < 2 )
8435 return;
8436
8438
8439 if( m_aGraphicsStack.front().m_aLineColor == COL_TRANSPARENT )
8440 return;
8441
8442 OStringBuffer aLine( 20 * nPoints );
8443 m_aPages.back().appendPolygon( rPoly, aLine, rPoly[0] == rPoly[nPoints-1] );
8444 aLine.append( "S\n" );
8445
8446 writeBuffer( aLine );
8447}
8448
8449void PDFWriterImpl::drawPolyLine( const tools::Polygon& rPoly, const LineInfo& rInfo )
8450{
8451 MARK( "drawPolyLine with LineInfo" );
8452
8454
8455 if( m_aGraphicsStack.front().m_aLineColor == COL_TRANSPARENT )
8456 return;
8457
8458 OStringBuffer aLine;
8459 aLine.append( "q " );
8460 if( m_aPages.back().appendLineInfo( rInfo, aLine ) )
8461 {
8462 writeBuffer( aLine );
8463 drawPolyLine( rPoly );
8464 writeBuffer( "Q\n" );
8465 }
8466 else
8467 {
8469 convertLineInfoToExtLineInfo( rInfo, aInfo );
8470 drawPolyLine( rPoly, aInfo );
8471 }
8472}
8473
8475{
8476 SAL_WARN_IF( rIn.GetStyle() != LineStyle::Dash, "vcl.pdfwriter", "invalid conversion" );
8477 rOut.m_fLineWidth = rIn.GetWidth();
8478 rOut.m_fTransparency = 0.0;
8481 rOut.m_fMiterLimit = 10;
8482 rOut.m_aDashArray = rIn.GetDotDashArray();
8483
8484 // add LineJoin
8485 switch(rIn.GetLineJoin())
8486 {
8488 {
8490 break;
8491 }
8492 // Pdf has no 'none' lineJoin, default is miter
8495 {
8497 break;
8498 }
8500 {
8502 break;
8503 }
8504 }
8505
8506 // add LineCap
8507 switch(rIn.GetLineCap())
8508 {
8509 default: /* css::drawing::LineCap_BUTT */
8510 {
8512 break;
8513 }
8514 case css::drawing::LineCap_ROUND:
8515 {
8517 break;
8518 }
8519 case css::drawing::LineCap_SQUARE:
8520 {
8522 break;
8523 }
8524 }
8525}
8526
8528{
8529 MARK( "drawPolyLine with ExtLineInfo" );
8530
8532
8533 if( m_aGraphicsStack.front().m_aLineColor == COL_TRANSPARENT )
8534 return;
8535
8536 if( rInfo.m_fTransparency >= 1.0 )
8537 return;
8538
8539 if( rInfo.m_fTransparency != 0.0 )
8541
8542 OStringBuffer aLine;
8543 aLine.append( "q " );
8544 m_aPages.back().appendMappedLength( rInfo.m_fLineWidth, aLine );
8545 aLine.append( " w" );
8546 if( rInfo.m_aDashArray.size() < 10 ) // implementation limit of acrobat reader
8547 {
8548 switch( rInfo.m_eCap )
8549 {
8550 default:
8551 case PDFWriter::capButt: aLine.append( " 0 J" );break;
8552 case PDFWriter::capRound: aLine.append( " 1 J" );break;
8553 case PDFWriter::capSquare: aLine.append( " 2 J" );break;
8554 }
8555 switch( rInfo.m_eJoin )
8556 {
8557 default:
8559 {
8560 double fLimit = rInfo.m_fMiterLimit;
8561 if( rInfo.m_fLineWidth < rInfo.m_fMiterLimit )
8562 fLimit = fLimit / rInfo.m_fLineWidth;
8563 if( fLimit < 1.0 )
8564 fLimit = 1.0;
8565 aLine.append( " 0 j " );
8566 appendDouble( fLimit, aLine );
8567 aLine.append( " M" );
8568 }
8569 break;
8570 case PDFWriter::joinRound: aLine.append( " 1 j" );break;
8571 case PDFWriter::joinBevel: aLine.append( " 2 j" );break;
8572 }
8573 if( !rInfo.m_aDashArray.empty() )
8574 {
8575 aLine.append( " [ " );
8576 for (auto const& dash : rInfo.m_aDashArray)
8577 {
8578 m_aPages.back().appendMappedLength( dash, aLine );
8579 aLine.append( ' ' );
8580 }
8581 aLine.append( "] 0 d" );
8582 }
8583 aLine.append( "\n" );
8584 writeBuffer( aLine );
8585 drawPolyLine( rPoly );
8586 }
8587 else
8588 {
8589 basegfx::B2DPolygon aPoly(rPoly.getB2DPolygon());
8590 basegfx::B2DPolyPolygon aPolyPoly;
8591
8592 basegfx::utils::applyLineDashing(aPoly, rInfo.m_aDashArray, &aPolyPoly);
8593
8594 // Old applyLineDashing subdivided the polygon. New one will create bezier curve segments.
8595 // To mimic old behaviour, apply subdivide here. If beziers shall be written (better quality)
8596 // this line needs to be removed and the loop below adapted accordingly
8597 aPolyPoly = basegfx::utils::adaptiveSubdivideByAngle(aPolyPoly);
8598
8599 const sal_uInt32 nPolygonCount(aPolyPoly.count());
8600
8601 for( sal_uInt32 nPoly = 0; nPoly < nPolygonCount; nPoly++ )
8602 {
8603 aLine.append( (nPoly != 0 && (nPoly & 7) == 0) ? "\n" : " " );
8604 aPoly = aPolyPoly.getB2DPolygon( nPoly );
8605 const sal_uInt32 nPointCount(aPoly.count());
8606
8607 if(nPointCount)
8608 {
8609 const sal_uInt32 nEdgeCount(aPoly.isClosed() ? nPointCount : nPointCount - 1);
8610 basegfx::B2DPoint aCurrent(aPoly.getB2DPoint(0));
8611
8612 for(sal_uInt32 a(0); a < nEdgeCount; a++)
8613 {
8614 if( a > 0 )
8615 aLine.append( " " );
8616 const sal_uInt32 nNextIndex((a + 1) % nPointCount);
8617 const basegfx::B2DPoint aNext(aPoly.getB2DPoint(nNextIndex));
8618
8619 m_aPages.back().appendPoint( Point( FRound(aCurrent.getX()),
8620 FRound(aCurrent.getY()) ),
8621 aLine );
8622 aLine.append( " m " );
8623 m_aPages.back().appendPoint( Point( FRound(aNext.getX()),
8624 FRound(aNext.getY()) ),
8625 aLine );
8626 aLine.append( " l" );
8627
8628 // prepare next edge
8629 aCurrent = aNext;
8630 }
8631 }
8632 }
8633 aLine.append( " S " );
8634 writeBuffer( aLine );
8635 }
8636 writeBuffer( "Q\n" );
8637
8638 if( rInfo.m_fTransparency == 0.0 )
8639 return;
8640
8641 // FIXME: actually this may be incorrect with bezier polygons
8642 tools::Rectangle aBoundRect( rPoly.GetBoundRect() );
8643 // avoid clipping with thick lines
8644 if( rInfo.m_fLineWidth > 0.0 )
8645 {
8646 sal_Int32 nLW = sal_Int32(rInfo.m_fLineWidth);
8647 aBoundRect.AdjustTop( -nLW );
8648 aBoundRect.AdjustLeft( -nLW );
8649 aBoundRect.AdjustRight(nLW );
8650 aBoundRect.AdjustBottom(nLW );
8651 }
8652 endTransparencyGroup( aBoundRect, static_cast<sal_uInt16>(100.0*rInfo.m_fTransparency) );
8653}
8654
8655void PDFWriterImpl::drawPixel( const Point& rPoint, const Color& rColor )
8656{
8657 MARK( "drawPixel" );
8658
8659 Color aColor = ( rColor == COL_TRANSPARENT ? m_aGraphicsStack.front().m_aLineColor : rColor );
8660
8661 if( aColor == COL_TRANSPARENT )
8662 return;
8663
8664 // pixels are drawn in line color, so have to set
8665 // the nonstroking color to line color
8666 Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor;
8667 setFillColor( aColor );
8668
8670
8671 OStringBuffer aLine( 20 );
8672 m_aPages.back().appendPoint( rPoint, aLine );
8673 aLine.append( ' ' );
8674 appendDouble( 1.0/double(GetDPIX()), aLine );
8675 aLine.append( ' ' );
8676 appendDouble( 1.0/double(GetDPIY()), aLine );
8677 aLine.append( " re f\n" );
8678 writeBuffer( aLine );
8679
8680 setFillColor( aOldFillColor );
8681}
8682
8684{
8685 CHECK_RETURN2( updateObject( rObject.m_nObject ) );
8686
8687 bool bFlateFilter = compressStream( rObject.m_pContentStream.get() );
8688 sal_uInt64 nSize = rObject.m_pContentStream->TellEnd();
8689 rObject.m_pContentStream->Seek( STREAM_SEEK_TO_BEGIN );
8691 {
8692 emitComment( "PDFWriterImpl::writeTransparentObject" );
8693 }
8694 OStringBuffer aLine( 512 );
8695 CHECK_RETURN2( updateObject( rObject.m_nObject ) );
8696 aLine.append( rObject.m_nObject );
8697 aLine.append( " 0 obj\n"
8698 "<</Type/XObject\n"
8699 "/Subtype/Form\n"
8700 "/BBox[ " );
8701 appendFixedInt( rObject.m_aBoundRect.Left(), aLine );
8702 aLine.append( ' ' );
8703 appendFixedInt( rObject.m_aBoundRect.Top(), aLine );
8704 aLine.append( ' ' );
8705 appendFixedInt( rObject.m_aBoundRect.Right(), aLine );
8706 aLine.append( ' ' );
8707 appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aLine );
8708 aLine.append( " ]\n" );
8709 if( ! m_bIsPDF_A1 )
8710 {
8711 // 7.8.3 Resource dicts are required for content streams
8712 aLine.append( "/Resources " );
8713 aLine.append( getResourceDictObj() );
8714 aLine.append( " 0 R\n" );
8715
8716 aLine.append( "/Group<</S/Transparency/CS/DeviceRGB/K true>>\n" );
8717 }
8718
8719 aLine.append( "/Length " );
8720 aLine.append( static_cast<sal_Int32>(nSize) );
8721 aLine.append( "\n" );
8722 if( bFlateFilter )
8723 aLine.append( "/Filter/FlateDecode\n" );
8724 aLine.append( ">>\n"
8725 "stream\n" );
8726 CHECK_RETURN2( writeBuffer( aLine ) );
8728 CHECK_RETURN2( writeBufferBytes( rObject.m_pContentStream->GetData(), nSize ) );
8730 aLine.setLength( 0 );
8731 aLine.append( "\n"
8732 "endstream\n"
8733 "endobj\n\n" );
8734 CHECK_RETURN2( writeBuffer( aLine ) );
8735
8736 // write ExtGState dict for this XObject
8737 aLine.setLength( 0 );
8738 aLine.append( rObject.m_nExtGStateObject );
8739 aLine.append( " 0 obj\n"
8740 "<<" );
8741
8742 if( m_bIsPDF_A1 )
8743 {
8744 aLine.append( "/CA 1.0/ca 1.0" );
8746 }
8747 else
8748 {
8749 aLine.append( "/CA " );
8750 appendDouble( rObject.m_fAlpha, aLine );
8751 aLine.append( "\n"
8752 " /ca " );
8753 appendDouble( rObject.m_fAlpha, aLine );
8754 }
8755 aLine.append( "\n" );
8756
8757 aLine.append( ">>\n"
8758 "endobj\n\n" );
8760 CHECK_RETURN2( writeBuffer( aLine ) );
8761}
8762
8764{
8765 // LO internal gradient -> PDF shading type:
8766 // * css::awt::GradientStyle_LINEAR: axial shading, using sampled-function with 2 samples
8767 // [t=0:colorStart, t=1:colorEnd]
8768 // * css::awt::GradientStyle_AXIAL: axial shading, using sampled-function with 3 samples
8769 // [t=0:colorEnd, t=0.5:colorStart, t=1:colorEnd]
8770 // * other styles: function shading with aSize.Width() * aSize.Height() samples
8771 sal_Int32 nFunctionObject = createObject();
8772 CHECK_RETURN( updateObject( nFunctionObject ) );
8773
8775 aDev->SetOutputSizePixel( rObject.m_aSize );
8776 aDev->SetMapMode( MapMode( MapUnit::MapPixel ) );
8778 aDev->SetDrawMode( aDev->GetDrawMode() |
8781 aDev->DrawGradient( tools::Rectangle( Point( 0, 0 ), rObject.m_aSize ), rObject.m_aGradient );
8782
8783 Bitmap aSample = aDev->GetBitmap( Point( 0, 0 ), rObject.m_aSize );
8784 Bitmap::ScopedReadAccess pAccess(aSample);
8785
8786 Size aSize = aSample.GetSizePixel();
8787
8788 sal_Int32 nStreamLengthObject = createObject();
8790 {
8791 emitComment( "PDFWriterImpl::writeGradientFunction" );
8792 }
8793 OStringBuffer aLine( 120 );
8794 aLine.append( nFunctionObject );
8795 aLine.append( " 0 obj\n"
8796 "<</FunctionType 0\n");
8797 switch (rObject.m_aGradient.GetStyle())
8798 {
8799 case css::awt::GradientStyle_LINEAR:
8800 case css::awt::GradientStyle_AXIAL:
8801 aLine.append("/Domain[ 0 1]\n");
8802 break;
8803 default:
8804 aLine.append("/Domain[ 0 1 0 1]\n");
8805 }
8806 aLine.append("/Size[ " );
8807 switch (rObject.m_aGradient.GetStyle())
8808 {
8809 case css::awt::GradientStyle_LINEAR:
8810 aLine.append('2');
8811 break;
8812 case css::awt::GradientStyle_AXIAL:
8813 aLine.append('3');
8814 break;
8815 default:
8816 aLine.append( static_cast<sal_Int32>(aSize.Width()) );
8817 aLine.append( ' ' );
8818 aLine.append( static_cast<sal_Int32>(aSize.Height()) );
8819 }
8820 aLine.append( " ]\n"
8821 "/BitsPerSample 8\n"
8822 "/Range[ 0 1 0 1 0 1 ]\n"
8823 "/Order 3\n"
8824 "/Length " );
8825 aLine.append( nStreamLengthObject );
8827 aLine.append( " 0 R\n"
8828 "/Filter/FlateDecode"
8829 ">>\n"
8830 "stream\n" );
8831 else
8832 aLine.append( " 0 R\n"
8833 ">>\n"
8834 "stream\n" );
8835 CHECK_RETURN( writeBuffer( aLine ) );
8836
8837 sal_uInt64 nStartStreamPos = 0;
8838 CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nStartStreamPos)) );
8839
8840 checkAndEnableStreamEncryption( nFunctionObject );
8842 sal_uInt8 aCol[3];
8843 switch (rObject.m_aGradient.GetStyle())
8844 {
8845 case css::awt::GradientStyle_AXIAL:
8846 aCol[0] = rObject.m_aGradient.GetEndColor().GetRed();
8847 aCol[1] = rObject.m_aGradient.GetEndColor().GetGreen();
8848 aCol[2] = rObject.m_aGradient.GetEndColor().GetBlue();
8849 CHECK_RETURN( writeBufferBytes( aCol, 3 ) );
8850 [[fallthrough]];
8851 case css::awt::GradientStyle_LINEAR:
8852 {
8853 aCol[0] = rObject.m_aGradient.GetStartColor().GetRed();
8854 aCol[1] = rObject.m_aGradient.GetStartColor().GetGreen();
8855 aCol[2] = rObject.m_aGradient.GetStartColor().GetBlue();
8856 CHECK_RETURN( writeBufferBytes( aCol, 3 ) );
8857
8858 aCol[0] = rObject.m_aGradient.GetEndColor().GetRed();
8859 aCol[1] = rObject.m_aGradient.GetEndColor().GetGreen();
8860 aCol[2] = rObject.m_aGradient.GetEndColor().GetBlue();
8861 CHECK_RETURN( writeBufferBytes( aCol, 3 ) );
8862 break;
8863 }
8864 default:
8865 for( int y = aSize.Height()-1; y >= 0; y-- )
8866 {
8867 for( tools::Long x = 0; x < aSize.Width(); x++ )
8868 {
8869 BitmapColor aColor = pAccess->GetColor( y, x );
8870 aCol[0] = aColor.GetRed();
8871 aCol[1] = aColor.GetGreen();
8872 aCol[2] = aColor.GetBlue();
8873 CHECK_RETURN( writeBufferBytes( aCol, 3 ) );
8874 }
8875 }
8876 }
8879
8880 sal_uInt64 nEndStreamPos = 0;
8881 CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nEndStreamPos)) );
8882
8883 aLine.setLength( 0 );
8884 aLine.append( "\nendstream\nendobj\n\n" );
8885 CHECK_RETURN( writeBuffer( aLine ) );
8886
8887 // write stream length
8888 CHECK_RETURN( updateObject( nStreamLengthObject ) );
8889 aLine.setLength( 0 );
8890 aLine.append( nStreamLengthObject );
8891 aLine.append( " 0 obj\n" );
8892 aLine.append( static_cast<sal_Int64>(nEndStreamPos-nStartStreamPos) );
8893 aLine.append( "\nendobj\n\n" );
8894 CHECK_RETURN( writeBuffer( aLine ) );
8895
8896 CHECK_RETURN( updateObject( rObject.m_nObject ) );
8897 aLine.setLength( 0 );
8898 aLine.append( rObject.m_nObject );
8899 aLine.append( " 0 obj\n");
8900 switch (rObject.m_aGradient.GetStyle())
8901 {
8902 case css::awt::GradientStyle_LINEAR:
8903 case css::awt::GradientStyle_AXIAL:
8904 aLine.append("<</ShadingType 2\n");
8905 break;
8906 default:
8907 aLine.append("<</ShadingType 1\n");
8908 }
8909 aLine.append("/ColorSpace/DeviceRGB\n"
8910 "/AntiAlias true\n");
8911
8912 // Determination of shading axis
8913 // See: OutputDevice::ImplDrawLinearGradient for reference
8914 tools::Rectangle aRect;
8915 aRect.SetLeft(0);
8916 aRect.SetTop(0);
8917 aRect.SetRight( aSize.Width() );
8918 aRect.SetBottom( aSize.Height() );
8919
8920 tools::Rectangle aBoundRect;
8921 Point aCenter;
8922 Degree10 nAngle = rObject.m_aGradient.GetAngle() % 3600_deg10;
8923 rObject.m_aGradient.GetBoundRect( aRect, aBoundRect, aCenter );
8924
8925 const bool bLinear = (rObject.m_aGradient.GetStyle() == css::awt::GradientStyle_LINEAR);
8926 double fBorder = aBoundRect.GetHeight() * rObject.m_aGradient.GetBorder() / 100.0;
8927 if ( !bLinear )
8928 {
8929 fBorder /= 2.0;
8930 }
8931
8932 aBoundRect.AdjustBottom( -fBorder );
8933 if (!bLinear)
8934 {
8935 aBoundRect.AdjustTop(fBorder );
8936 }
8937
8938 switch (rObject.m_aGradient.GetStyle())
8939 {
8940 case css::awt::GradientStyle_LINEAR:
8941 case css::awt::GradientStyle_AXIAL:
8942 {
8943 aLine.append("/Domain[ 0 1 ]\n"
8944 "/Coords[ " );
8945 tools::Polygon aPoly( 2 );
8946 aPoly[0] = aBoundRect.BottomCenter();
8947 aPoly[1] = aBoundRect.TopCenter();
8948 aPoly.Rotate( aCenter, 3600_deg10 - nAngle );
8949
8950 aLine.append( static_cast<sal_Int32>(aPoly[0].X()) );
8951 aLine.append( " " );
8952 aLine.append( static_cast<sal_Int32>(aPoly[0].Y()) );
8953 aLine.append( " " );
8954 aLine.append( static_cast<sal_Int32>(aPoly[1].X()));
8955 aLine.append( " ");
8956 aLine.append( static_cast<sal_Int32>(aPoly[1].Y()));
8957 aLine.append( " ]\n");
8958 aLine.append("/Extend [true true]\n");
8959 break;
8960 }
8961 default:
8962 aLine.append("/Domain[ 0 1 0 1 ]\n"
8963 "/Matrix[ " );
8964 aLine.append( static_cast<sal_Int32>(aSize.Width()) );
8965 aLine.append( " 0 0 " );
8966 aLine.append( static_cast<sal_Int32>(aSize.Height()) );
8967 aLine.append( " 0 0 ]\n");
8968 }
8969 aLine.append("/Function " );
8970 aLine.append( nFunctionObject );
8971 aLine.append( " 0 R\n"
8972 ">>\n"
8973 "endobj\n\n" );
8974 return writeBuffer( aLine );
8975}
8976
8977void PDFWriterImpl::writeJPG( const JPGEmit& rObject )
8978{
8980 {
8982 return;
8983 }
8984
8985 CHECK_RETURN2( rObject.m_pStream );
8986 CHECK_RETURN2( updateObject( rObject.m_nObject ) );
8987
8988 sal_Int32 nLength = rObject.m_pStream->TellEnd();
8989 rObject.m_pStream->Seek( STREAM_SEEK_TO_BEGIN );
8990
8991 sal_Int32 nMaskObject = 0;
8992 if( !rObject.m_aAlphaMask.IsEmpty() )
8993 {
8995 && !m_bIsPDF_A1)
8996 {
8997 nMaskObject = createObject();
8998 }
8999 else if( m_bIsPDF_A1 )
9003
9004 }
9006 {
9007 emitComment( "PDFWriterImpl::writeJPG" );
9008 }
9009
9010 OStringBuffer aLine(200);
9011 aLine.append( rObject.m_nObject );
9012 aLine.append( " 0 obj\n"
9013 "<</Type/XObject/Subtype/Image/Width " );
9014 aLine.append( static_cast<sal_Int32>(rObject.m_aID.m_aPixelSize.Width()) );
9015 aLine.append( " /Height " );
9016 aLine.append( static_cast<sal_Int32>(rObject.m_aID.m_aPixelSize.Height()) );
9017 aLine.append( " /BitsPerComponent 8 " );
9018 if( rObject.m_bTrueColor )
9019 aLine.append( "/ColorSpace/DeviceRGB" );
9020 else
9021 aLine.append( "/ColorSpace/DeviceGray" );
9022 aLine.append( "/Filter/DCTDecode/Length " );
9023 aLine.append( nLength );
9024 if( nMaskObject )
9025 {
9026 aLine.append(" /SMask ");
9027 aLine.append( nMaskObject );
9028 aLine.append( " 0 R " );
9029 }
9030 aLine.append( ">>\nstream\n" );
9031 CHECK_RETURN2( writeBuffer( aLine ) );
9032
9034 CHECK_RETURN2( writeBufferBytes( rObject.m_pStream->GetData(), nLength ) );
9036
9037 aLine.setLength( 0 );
9038 CHECK_RETURN2( writeBuffer( "\nendstream\nendobj\n\n" ) );
9039
9040 if( nMaskObject )
9041 {
9042 BitmapEmit aEmit;
9043 aEmit.m_nObject = nMaskObject;
9044 aEmit.m_aBitmap = BitmapEx( rObject.m_aAlphaMask, rObject.m_aAlphaMask );
9045 writeBitmapObject( aEmit, true );
9046 }
9047
9049}
9050
9052{
9053 if (rEmit.m_nFormObject <= 0)
9054 return;
9055
9056 // Count /Matrix and /BBox.
9057 // vcl::ImportPDF() uses getDefaultPdfResolutionDpi to set the desired
9058 // rendering DPI so we have to take into account that here too.
9059 static const double fResolutionDPI = vcl::pdf::getDefaultPdfResolutionDpi();
9060
9061 sal_Int32 nOldDPIX = GetDPIX();
9062 sal_Int32 nOldDPIY = GetDPIY();
9063 SetDPIX(fResolutionDPI);
9064 SetDPIY(fResolutionDPI);
9066 SetDPIX(nOldDPIX);
9067 SetDPIY(nOldDPIY);
9068 double fScaleX = 1.0 / aSize.Width();
9069 double fScaleY = 1.0 / aSize.Height();
9070
9071 sal_Int32 nWrappedFormObject = 0;
9073 {
9074 // Parse the PDF data, we need that to write the PDF dictionary of our
9075 // object.
9076 if (rEmit.m_nExternalPDFDataIndex < 0)
9077 return;
9078 auto& rExternalPDFStream = m_aExternalPDFStreams.get(rEmit.m_nExternalPDFDataIndex);
9079 auto& pPDFDocument = rExternalPDFStream.getPDFDocument();
9080 if (!pPDFDocument)
9081 {
9082 // Couldn't parse the document and can't continue
9083 SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: failed to parse the document");
9084 return;
9085 }
9086
9087 std::vector<filter::PDFObjectElement*> aPages = pPDFDocument->GetPages();
9088 if (aPages.empty())
9089 {
9090 SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: no pages");
9091 return;
9092 }
9093
9094 size_t nPageIndex = rEmit.m_nExternalPDFPageIndex >= 0 ? rEmit.m_nExternalPDFPageIndex : 0;
9095
9096 filter::PDFObjectElement* pPage = aPages[nPageIndex];
9097 if (!pPage)
9098 {
9099 SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: no page");
9100 return;
9101 }
9102
9103 double aOrigin[2] = { 0.0, 0.0 };
9104 if (auto* pArray = dynamic_cast<filter::PDFArrayElement*>(pPage->Lookup("MediaBox")))
9105 {
9106 const auto& rElements = pArray->GetElements();
9107 if (rElements.size() >= 4)
9108 {
9109 // get x1, y1 of the rectangle.
9110 for (sal_Int32 nIdx = 0; nIdx < 2; ++nIdx)
9111 {
9112 if (const auto* pNumElement = dynamic_cast<filter::PDFNumberElement*>(rElements[nIdx]))
9113 aOrigin[nIdx] = pNumElement->GetValue();
9114 }
9115 }
9116 }
9117
9118 std::vector<filter::PDFObjectElement*> aContentStreams;
9119 if (filter::PDFObjectElement* pContentStream = pPage->LookupObject("Contents"))
9120 aContentStreams.push_back(pContentStream);
9121 else if (auto pArray = dynamic_cast<filter::PDFArrayElement*>(pPage->Lookup("Contents")))
9122 {
9123 for (const auto pElement : pArray->GetElements())
9124 {
9125 auto pReference = dynamic_cast<filter::PDFReferenceElement*>(pElement);
9126 if (!pReference)
9127 continue;
9128
9129 filter::PDFObjectElement* pObject = pReference->LookupObject();
9130 if (!pObject)
9131 continue;
9132
9133 aContentStreams.push_back(pObject);
9134 }
9135 }
9136
9137 if (aContentStreams.empty())
9138 {
9139 SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: no content stream");
9140 return;
9141 }
9142
9143 // Merge link annotations from pPage to our page.
9144 std::vector<filter::PDFObjectElement*> aAnnots;
9145 if (auto pArray = dynamic_cast<filter::PDFArrayElement*>(pPage->Lookup("Annots")))
9146 {
9147 for (const auto pElement : pArray->GetElements())
9148 {
9149 auto pReference = dynamic_cast<filter::PDFReferenceElement*>(pElement);
9150 if (!pReference)
9151 {
9152 continue;
9153 }
9154
9155 filter::PDFObjectElement* pObject = pReference->LookupObject();
9156 if (!pObject)
9157 {
9158 continue;
9159 }
9160
9161 auto pType = dynamic_cast<filter::PDFNameElement*>(pObject->Lookup("Type"));
9162 if (!pType || pType->GetValue() != "Annot")
9163 {
9164 continue;
9165 }
9166
9167 auto pSubtype = dynamic_cast<filter::PDFNameElement*>(pObject->Lookup("Subtype"));
9168 if (!pSubtype || pSubtype->GetValue() != "Link")
9169 {
9170 continue;
9171 }
9172
9173 // Reference to a link annotation object, remember it.
9174 aAnnots.push_back(pObject);
9175 }
9176 }
9177 if (!aAnnots.empty())
9178 {
9179 PDFObjectCopier aCopier(*this);
9180 SvMemoryStream& rDocBuffer = pPage->GetDocument().GetEditBuffer();
9181 std::map<sal_Int32, sal_Int32> aMap;
9182 for (const auto& pAnnot : aAnnots)
9183 {
9184 // Copy over the annotation and refer to its new id.
9185 sal_Int32 nNewId = aCopier.copyExternalResource(rDocBuffer, *pAnnot, aMap);
9186 m_aPages.back().m_aAnnotations.push_back(nNewId);
9187 }
9188 }
9189
9190 nWrappedFormObject = createObject();
9191 // Write the form XObject wrapped below. This is a separate object from
9192 // the wrapper, this way there is no need to alter the stream contents.
9193
9194 OStringBuffer aLine;
9195 aLine.append(nWrappedFormObject);
9196 aLine.append(" 0 obj\n");
9197 aLine.append("<< /Type /XObject");
9198 aLine.append(" /Subtype /Form");
9199
9200 tools::Long nWidth = aSize.Width();
9201 tools::Long nHeight = aSize.Height();
9202 basegfx::B2DRange aBBox(0, 0, aSize.Width(), aSize.Height());
9203 if (auto pRotate = dynamic_cast<filter::PDFNumberElement*>(pPage->Lookup("Rotate")))
9204 {
9205 // The original page was rotated, then construct a transformation matrix which does the
9206 // same with our form object.
9207 sal_Int32 nRotAngle = static_cast<sal_Int32>(pRotate->GetValue()) % 360;
9208 // /Rotate is clockwise, matrix rotate is counter-clockwise.
9209 sal_Int32 nAngle = -1 * nRotAngle;
9210
9211 // The bounding box just rotates.
9212 basegfx::B2DHomMatrix aBBoxMat;
9213 aBBoxMat.rotate(basegfx::deg2rad(pRotate->GetValue()));
9214 aBBox.transform(aBBoxMat);
9215
9216 // Now transform the object: rotate around the center and make sure that the rotation
9217 // doesn't affect the aspect ratio.
9219 aMat.translate(-0.5 * aBBox.getWidth() - aOrigin[0], -0.5 * aBBox.getHeight() - aOrigin[1]);
9220 aMat.rotate(basegfx::deg2rad(nAngle));
9221 aMat.translate(0.5 * nWidth, 0.5 * nHeight);
9222
9223 aLine.append(" /Matrix [ ");
9224 aLine.append(aMat.a());
9225 aLine.append(" ");
9226 aLine.append(aMat.b());
9227 aLine.append(" ");
9228 aLine.append(aMat.c());
9229 aLine.append(" ");
9230 aLine.append(aMat.d());
9231 aLine.append(" ");
9232 aLine.append(aMat.e());
9233 aLine.append(" ");
9234 aLine.append(aMat.f());
9235 aLine.append(" ] ");
9236 }
9237
9238 PDFObjectCopier aCopier(*this);
9239 auto & rResources = rExternalPDFStream.getCopiedResources();
9240 aCopier.copyPageResources(pPage, aLine, rResources);
9241
9242 aLine.append(" /BBox [ ");
9243 aLine.append(aOrigin[0]);
9244 aLine.append(' ');
9245 aLine.append(aOrigin[1]);
9246 aLine.append(' ');
9247 aLine.append(aBBox.getWidth() + aOrigin[0]);
9248 aLine.append(' ');
9249 aLine.append(aBBox.getHeight() + aOrigin[1]);
9250 aLine.append(" ]");
9251
9253 aLine.append(" /Filter/FlateDecode");
9254 aLine.append(" /Length ");
9255
9256 SvMemoryStream aStream;
9257 bool bCompressed = false;
9258 sal_Int32 nLength = PDFObjectCopier::copyPageStreams(aContentStreams, aStream, bCompressed);
9259 aLine.append(nLength);
9260
9261 aLine.append(">>\nstream\n");
9263 {
9264 emitComment("PDFWriterImpl::writeReferenceXObject, WrappedFormObject");
9265 }
9266 if (!updateObject(nWrappedFormObject))
9267 return;
9268 if (!writeBuffer(aLine))
9269 return;
9270 aLine.setLength(0);
9271
9272 checkAndEnableStreamEncryption(nWrappedFormObject);
9273 // Copy the original page streams to the form XObject stream.
9274 aLine.append(static_cast<const char*>(aStream.GetData()), aStream.GetSize());
9275 if (!writeBuffer(aLine))
9276 return;
9277 aLine.setLength(0);
9279
9280 aLine.append("\nendstream\nendobj\n\n");
9281 if (!writeBuffer(aLine))
9282 return;
9283 }
9284
9285 OStringBuffer aLine;
9287 {
9288 emitComment("PDFWriterImpl::writeReferenceXObject, FormObject");
9289 }
9290 if (!updateObject(rEmit.m_nFormObject))
9291 return;
9292
9293 // Now have all the info to write the form XObject.
9294 aLine.append(rEmit.m_nFormObject);
9295 aLine.append(" 0 obj\n");
9296 aLine.append("<< /Type /XObject");
9297 aLine.append(" /Subtype /Form");
9298 aLine.append(" /Resources << /XObject<<");
9299
9300 sal_Int32 nObject = m_aContext.UseReferenceXObject ? rEmit.m_nBitmapObject : nWrappedFormObject;
9301 aLine.append(" /Im");
9302 aLine.append(nObject);
9303 aLine.append(" ");
9304 aLine.append(nObject);
9305 aLine.append(" 0 R");
9306
9307 aLine.append(">> >>");
9308 aLine.append(" /Matrix [ ");
9309 appendDouble(fScaleX, aLine);
9310 aLine.append(" 0 0 ");
9311 appendDouble(fScaleY, aLine);
9312 aLine.append(" 0 0 ]");
9313 aLine.append(" /BBox [ 0 0 ");
9314 aLine.append(aSize.Width());
9315 aLine.append(" ");
9316 aLine.append(aSize.Height());
9317 aLine.append(" ]\n");
9318
9320 {
9321 // Write the reference dictionary.
9322 aLine.append("/Ref<< /F << /Type /Filespec /F (<embedded file>) ");
9324 { // ISO 14289-1:2014, Clause: 7.11
9325 aLine.append("/UF (<embedded file>) ");
9326 }
9327 aLine.append("/EF << /F ");
9328 aLine.append(rEmit.m_nEmbeddedObject);
9329 aLine.append(" 0 R >> >> /Page 0 >>\n");
9330 }
9331
9332 aLine.append("/Length ");
9333
9334 OStringBuffer aStream;
9335 aStream.append("q ");
9337 {
9338 // Reference XObject markup is used, just refer to the fallback bitmap
9339 // here.
9340 aStream.append(aSize.Width());
9341 aStream.append(" 0 0 ");
9342 aStream.append(aSize.Height());
9343 aStream.append(" 0 0 cm\n");
9344 aStream.append("/Im");
9345 aStream.append(rEmit.m_nBitmapObject);
9346 aStream.append(" Do\n");
9347 }
9348 else
9349 {
9350 // Reset line width to the default.
9351 aStream.append(" 1 w\n");
9352
9353 // vcl::RenderPDFBitmaps() effectively renders a white background for transparent input, be
9354 // consistent with that.
9355 aStream.append("1 1 1 rg\n");
9356 aStream.append("0 0 ");
9357 aStream.append(aSize.Width());
9358 aStream.append(" ");
9359 aStream.append(aSize.Height());
9360 aStream.append(" re\n");
9361 aStream.append("f*\n");
9362
9363 // Reset non-stroking color in case the XObject uses the default
9364 aStream.append("0 0 0 rg\n");
9365 // No reference XObject, draw the form XObject containing the original
9366 // page streams.
9367 aStream.append("/Im");
9368 aStream.append(nWrappedFormObject);
9369 aStream.append(" Do\n");
9370 }
9371 aStream.append("Q");
9372 aLine.append(aStream.getLength());
9373
9374 aLine.append(">>\nstream\n");
9375 if (!writeBuffer(aLine))
9376 return;
9377 aLine.setLength(0);
9378
9380 aLine.append(aStream.getStr());
9381 if (!writeBuffer(aLine))
9382 return;
9383 aLine.setLength(0);
9385
9386 aLine.append("\nendstream\nendobj\n\n");
9387 CHECK_RETURN2(writeBuffer(aLine));
9388}
9389
9390bool PDFWriterImpl::writeBitmapObject( const BitmapEmit& rObject, bool bMask )
9391{
9393 {
9395 return true;
9396 }
9397
9398 CHECK_RETURN( updateObject( rObject.m_nObject ) );
9399
9400 Bitmap aBitmap;
9401 bool bWriteMask = false;
9402 if( ! bMask )
9403 {
9404 aBitmap = rObject.m_aBitmap.GetBitmap();
9405 if( rObject.m_aBitmap.IsAlpha() )
9406 {
9408 bWriteMask = true;
9409 // else draw without alpha channel
9410 }
9411 }
9412 else
9413 {
9415 {
9416 if( rObject.m_aBitmap.IsAlpha() )
9417 {
9418 aBitmap = rObject.m_aBitmap.GetAlphaMask();
9420 SAL_WARN_IF(aBitmap.getPixelFormat() != vcl::PixelFormat::N8_BPP, "vcl.pdfwriter", "mask conversion failed" );
9421 }
9422 }
9423 else if (aBitmap.getPixelFormat() != vcl::PixelFormat::N8_BPP)
9424 {
9425 aBitmap = rObject.m_aBitmap.GetAlphaMask().GetBitmap();
9427 SAL_WARN_IF(aBitmap.getPixelFormat() != vcl::PixelFormat::N8_BPP, "vcl.pdfwriter", "alpha mask conversion failed" );
9428 }
9429 }
9430
9431 Bitmap::ScopedReadAccess pAccess(aBitmap);
9432
9433 bool bTrueColor = true;
9434 sal_Int32 nBitsPerComponent = 0;
9435 auto const ePixelFormat = aBitmap.getPixelFormat();
9436 switch (ePixelFormat)
9437 {
9439 bTrueColor = false;
9440 nBitsPerComponent = vcl::pixelFormatBitCount(ePixelFormat);
9441 break;
9444 bTrueColor = true;
9445 nBitsPerComponent = 8;
9446 break;
9448 return false;
9449 }
9450
9451 sal_Int32 nStreamLengthObject = createObject();
9452 sal_Int32 nMaskObject = 0;
9453
9455 {
9456 emitComment( "PDFWriterImpl::writeBitmapObject" );
9457 }
9458 OStringBuffer aLine(1024);
9459 aLine.append( rObject.m_nObject );
9460 aLine.append( " 0 obj\n"
9461 "<</Type/XObject/Subtype/Image/Width " );
9462 aLine.append( static_cast<sal_Int32>(aBitmap.GetSizePixel().Width()) );
9463 aLine.append( "/Height " );
9464 aLine.append( static_cast<sal_Int32>(aBitmap.GetSizePixel().Height()) );
9465 aLine.append( "/BitsPerComponent " );
9466 aLine.append( nBitsPerComponent );
9467 aLine.append( "/Length " );
9468 aLine.append( nStreamLengthObject );
9469 aLine.append( " 0 R\n" );
9471 {
9472 if( nBitsPerComponent != 1 )
9473 {
9474 aLine.append( "/Filter/FlateDecode" );
9475 }
9476 else
9477 {
9478 aLine.append( "/Filter/CCITTFaxDecode/DecodeParms<</K -1/BlackIs1 true/Columns " );
9479 aLine.append( static_cast<sal_Int32>(aBitmap.GetSizePixel().Width()) );
9480 aLine.append( ">>\n" );
9481 }
9482 }
9483 if( ! bMask )
9484 {
9485 aLine.append( "/ColorSpace" );
9486 if( bTrueColor )
9487 aLine.append( "/DeviceRGB\n" );
9488 else if( aBitmap.HasGreyPaletteAny() )
9489 {
9490 aLine.append( "/DeviceGray\n" );
9492 {
9493 // #i47395# 1 bit bitmaps occasionally have an inverted grey palette
9494 sal_uInt16 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( COL_BLACK ) );
9495 assert( nBlackIndex == 0 || nBlackIndex == 1);
9496 sal_uInt16 nWhiteIndex = pAccess->GetBestPaletteIndex( BitmapColor( COL_WHITE ) );
9497 if( pAccess->GetPalette()[nBlackIndex] == BitmapColor( COL_BLACK ) &&
9498 pAccess->GetPalette()[nWhiteIndex] == BitmapColor( COL_WHITE ) )
9499 {
9500 // It is black and white
9501 if( nBlackIndex == 1 )
9502 aLine.append( "/Decode[1 0]\n" );
9503 }
9504 else
9505 {
9506 // It is two levels of grey
9507 aLine.append( "/Decode[" );
9508 assert( pAccess->GetPalette()[0].GetRed() == pAccess->GetPalette()[0].GetGreen() &&
9509 pAccess->GetPalette()[0].GetRed() == pAccess->GetPalette()[0].GetBlue() &&
9510 pAccess->GetPalette()[1].GetRed() == pAccess->GetPalette()[1].GetGreen() &&
9511 pAccess->GetPalette()[1].GetRed() == pAccess->GetPalette()[1].GetBlue() );
9512 aLine.append( pAccess->GetPalette()[0].GetRed() / 255.0 );
9513 aLine.append( " " );
9514 aLine.append( pAccess->GetPalette()[1].GetRed() / 255.0 );
9515 aLine.append( "]\n" );
9516 }
9517 }
9518 }
9519 else
9520 {
9521 aLine.append( "[ /Indexed/DeviceRGB " );
9522 aLine.append( static_cast<sal_Int32>(pAccess->GetPaletteEntryCount()-1) );
9523 aLine.append( "\n<" );
9525 {
9527 //check encryption buffer size
9528 m_vEncryptionBuffer.resize(pAccess->GetPaletteEntryCount()*3);
9529 int nChar = 0;
9530 //fill the encryption buffer
9531 for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
9532 {
9533 const BitmapColor& rColor = pAccess->GetPaletteColor( i );
9534 m_vEncryptionBuffer[nChar++] = rColor.GetRed();
9535 m_vEncryptionBuffer[nChar++] = rColor.GetGreen();
9536 m_vEncryptionBuffer[nChar++] = rColor.GetBlue();
9537 }
9538 //encrypt the colorspace lookup table
9539 rtl_cipher_encodeARCFOUR( m_aCipher, m_vEncryptionBuffer.data(), nChar, m_vEncryptionBuffer.data(), nChar );
9540 //now queue the data for output
9541 nChar = 0;
9542 for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
9543 {
9544 appendHex(m_vEncryptionBuffer[nChar++], aLine );
9545 appendHex(m_vEncryptionBuffer[nChar++], aLine );
9546 appendHex(m_vEncryptionBuffer[nChar++], aLine );
9547 }
9548 }
9549 else //no encryption requested (PDF/A-1a program flow drops here)
9550 {
9551 for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
9552 {
9553 const BitmapColor& rColor = pAccess->GetPaletteColor( i );
9554 appendHex( rColor.GetRed(), aLine );
9555 appendHex( rColor.GetGreen(), aLine );
9556 appendHex( rColor.GetBlue(), aLine );
9557 }
9558 }
9559 aLine.append( ">\n]\n" );
9560 }
9561 }
9562 else
9563 {
9564 aLine.append( "/ColorSpace/DeviceGray\n"
9565 "/Decode [ 1 0 ]\n" );
9566 }
9567
9568 if (!bMask && !m_bIsPDF_A1)
9569 {
9570 if( bWriteMask )
9571 {
9572 nMaskObject = createObject();
9573 if (rObject.m_aBitmap.IsAlpha())
9574 aLine.append( "/SMask " );
9575 else
9576 aLine.append( "/Mask " );
9577 aLine.append( nMaskObject );
9578 aLine.append( " 0 R\n" );
9579 }
9580 }
9581 else if( m_bIsPDF_A1 && bWriteMask )
9583
9584 aLine.append( ">>\n"
9585 "stream\n" );
9586 CHECK_RETURN( writeBuffer( aLine ) );
9587 sal_uInt64 nStartPos = 0;
9588 CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nStartPos)) );
9589
9591 if (!g_bDebugDisableCompression && nBitsPerComponent == 1)
9592 {
9593 writeG4Stream(pAccess.get());
9594 }
9595 else
9596 {
9598 if( ! bTrueColor || pAccess->GetScanlineFormat() == ScanlineFormat::N24BitTcRgb )
9599 {
9600 //With PDF bitmaps, each row is padded to a BYTE boundary (multiple of 8 bits).
9601 const int nScanLineBytes = ((pAccess->GetBitCount() * pAccess->Width()) + 7U) / 8U;
9602
9603 for( tools::Long i = 0; i < pAccess->Height(); i++ )
9604 {
9605 CHECK_RETURN( writeBufferBytes( pAccess->GetScanline( i ), nScanLineBytes ) );
9606 }
9607 }
9608 else
9609 {
9610 const int nScanLineBytes = pAccess->Width()*3;
9611 std::unique_ptr<sal_uInt8[]> xCol(new sal_uInt8[nScanLineBytes]);
9612 for( tools::Long y = 0; y < pAccess->Height(); y++ )
9613 {
9614 for( tools::Long x = 0; x < pAccess->Width(); x++ )
9615 {
9616 BitmapColor aColor = pAccess->GetColor( y, x );
9617 xCol[3*x+0] = aColor.GetRed();
9618 xCol[3*x+1] = aColor.GetGreen();
9619 xCol[3*x+2] = aColor.GetBlue();
9620 }
9621 CHECK_RETURN(writeBufferBytes(xCol.get(), nScanLineBytes));
9622 }
9623 }
9625 }
9627
9628 sal_uInt64 nEndPos = 0;
9629 CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nEndPos)) );
9630 aLine.setLength( 0 );
9631 aLine.append( "\nendstream\nendobj\n\n" );
9632 CHECK_RETURN( writeBuffer( aLine ) );
9633 CHECK_RETURN( updateObject( nStreamLengthObject ) );
9634 aLine.setLength( 0 );
9635 aLine.append( nStreamLengthObject );
9636 aLine.append( " 0 obj\n" );
9637 aLine.append( static_cast<sal_Int64>(nEndPos-nStartPos) );
9638 aLine.append( "\nendobj\n\n" );
9639 CHECK_RETURN( writeBuffer( aLine ) );
9640
9641 if( nMaskObject )
9642 {
9643 BitmapEmit aEmit;
9644 aEmit.m_nObject = nMaskObject;
9645 aEmit.m_aBitmap = rObject.m_aBitmap;
9646 return writeBitmapObject( aEmit, true );
9647 }
9648
9650
9651 return true;
9652}
9653
9654void PDFWriterImpl::createEmbeddedFile(const Graphic& rGraphic, ReferenceXObjectEmit& rEmit, sal_Int32 nBitmapObject)
9655{
9656 // The bitmap object is always a valid identifier, even if the graphic has
9657 // no pdf data.
9658 rEmit.m_nBitmapObject = nBitmapObject;
9659
9660 if (!rGraphic.getVectorGraphicData() || rGraphic.getVectorGraphicData()->getType() != VectorGraphicDataType::Pdf)
9661 return;
9662
9663 BinaryDataContainer const & rDataContainer = rGraphic.getVectorGraphicData()->getBinaryDataContainer();
9664
9666 {
9667 // Store the original PDF data as an embedded file.
9668 auto nObjectID = addEmbeddedFile(rDataContainer);
9669 rEmit.m_nEmbeddedObject = nObjectID;
9670 }
9671 else
9672 {
9673 sal_Int32 aIndex = m_aExternalPDFStreams.store(rDataContainer);
9674 rEmit.m_nExternalPDFPageIndex = rGraphic.getVectorGraphicData()->getPageIndex();
9676 }
9677
9678 rEmit.m_nFormObject = createObject();
9679 rEmit.m_aPixelSize = rGraphic.GetPrefSize();
9680}
9681
9682void PDFWriterImpl::drawJPGBitmap( SvStream& rDCTData, bool bIsTrueColor, const Size& rSizePixel, const tools::Rectangle& rTargetArea, const AlphaMask& rAlphaMask, const Graphic& rGraphic )
9683{
9684 MARK( "drawJPGBitmap" );
9685
9686 OStringBuffer aLine( 80 );
9688
9689 // #i40055# sanity check
9690 if( ! (rTargetArea.GetWidth() && rTargetArea.GetHeight() ) )
9691 return;
9692 if( ! (rSizePixel.Width() && rSizePixel.Height()) )
9693 return;
9694
9695 rDCTData.Seek( 0 );
9696 if( bIsTrueColor && m_aContext.ColorMode == PDFWriter::DrawGreyscale )
9697 {
9698 // need to convert to grayscale;
9699 // load stream to bitmap and draw the bitmap instead
9700 Graphic aGraphic;
9701 GraphicConverter::Import( rDCTData, aGraphic, ConvertDataFormat::JPG );
9702 if( !rAlphaMask.IsEmpty() && rAlphaMask.GetSizePixel() == aGraphic.GetSizePixel() )
9703 {
9704 Bitmap aBmp( aGraphic.GetBitmapEx().GetBitmap() );
9705 BitmapEx aBmpEx( aBmp, rAlphaMask );
9706 drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aBmpEx );
9707 }
9708 else
9709 drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aGraphic.GetBitmapEx() );
9710 return;
9711 }
9712
9713 std::unique_ptr<SvMemoryStream> pStream(new SvMemoryStream);
9714 pStream->WriteStream( rDCTData );
9715 pStream->Seek( STREAM_SEEK_TO_END );
9716
9717 BitmapID aID;
9718 aID.m_aPixelSize = rSizePixel;
9719 aID.m_nSize = pStream->Tell();
9720 pStream->Seek( STREAM_SEEK_TO_BEGIN );
9721 aID.m_nChecksum = vcl_get_checksum( 0, pStream->GetData(), aID.m_nSize );
9722 if( ! rAlphaMask.IsEmpty() )
9723 aID.m_nMaskChecksum = rAlphaMask.GetChecksum();
9724
9725 std::vector< JPGEmit >::const_iterator it = std::find_if(m_aJPGs.begin(), m_aJPGs.end(),
9726 [&](const JPGEmit& arg) { return aID == arg.m_aID; });
9727 if( it == m_aJPGs.end() )
9728 {
9729 m_aJPGs.emplace( m_aJPGs.begin() );
9730 JPGEmit& rEmit = m_aJPGs.front();
9732 rEmit.m_nObject = createObject();
9733 rEmit.m_aID = aID;
9734 rEmit.m_pStream = std::move( pStream );
9735 rEmit.m_bTrueColor = bIsTrueColor;
9736 if( !rAlphaMask.IsEmpty() && rAlphaMask.GetSizePixel() == rSizePixel )
9737 rEmit.m_aAlphaMask = rAlphaMask;
9738 createEmbeddedFile(rGraphic, rEmit.m_aReferenceXObject, rEmit.m_nObject);
9739
9740 it = m_aJPGs.begin();
9741 }
9742
9743 aLine.append( "q " );
9744 sal_Int32 nCheckWidth = 0;
9745 m_aPages.back().appendMappedLength( static_cast<sal_Int32>(rTargetArea.GetWidth()), aLine, false, &nCheckWidth );
9746 aLine.append( " 0 0 " );
9747 sal_Int32 nCheckHeight = 0;
9748 m_aPages.back().appendMappedLength( static_cast<sal_Int32>(rTargetArea.GetHeight()), aLine, true, &nCheckHeight );
9749 aLine.append( ' ' );
9750 m_aPages.back().appendPoint( rTargetArea.BottomLeft(), aLine );
9751 aLine.append( " cm\n/Im" );
9752 sal_Int32 nObject = it->m_aReferenceXObject.getObject();
9753 aLine.append(nObject);
9754 aLine.append( " Do Q\n" );
9755 if( nCheckWidth == 0 || nCheckHeight == 0 )
9756 {
9757 // #i97512# avoid invalid current matrix
9758 aLine.setLength( 0 );
9759 aLine.append( "\n%jpeg image /Im" );
9760 aLine.append( it->m_nObject );
9761 aLine.append( " scaled to zero size, omitted\n" );
9762 }
9763 writeBuffer( aLine );
9764
9765 OString aObjName = "Im" + OString::number(nObject);
9766 pushResource( ResourceKind::XObject, aObjName, nObject );
9767
9768}
9769
9770void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEmit& rBitmap, const Color& rFillColor )
9771{
9772 OStringBuffer aLine( 80 );
9774
9775 aLine.append( "q " );
9776 if( rFillColor != COL_TRANSPARENT )
9777 {
9778 appendNonStrokingColor( rFillColor, aLine );
9779 aLine.append( ' ' );
9780 }
9781 sal_Int32 nCheckWidth = 0;
9782 m_aPages.back().appendMappedLength( static_cast<sal_Int32>(rDestSize.Width()), aLine, false, &nCheckWidth );
9783 aLine.append( " 0 0 " );
9784 sal_Int32 nCheckHeight = 0;
9785 m_aPages.back().appendMappedLength( static_cast<sal_Int32>(rDestSize.Height()), aLine, true, &nCheckHeight );
9786 aLine.append( ' ' );
9787 m_aPages.back().appendPoint( rDestPoint + Point( 0, rDestSize.Height()-1 ), aLine );
9788 aLine.append( " cm\n/Im" );
9789 sal_Int32 nObject = rBitmap.m_aReferenceXObject.getObject();
9790 aLine.append(nObject);
9791 aLine.append( " Do Q\n" );
9792 if( nCheckWidth == 0 || nCheckHeight == 0 )
9793 {
9794 // #i97512# avoid invalid current matrix
9795 aLine.setLength( 0 );
9796 aLine.append( "\n%bitmap image /Im" );
9797 aLine.append( rBitmap.m_nObject );
9798 aLine.append( " scaled to zero size, omitted\n" );
9799 }
9800 writeBuffer( aLine );
9801}
9802
9803const BitmapEmit& PDFWriterImpl::createBitmapEmit(const BitmapEx& i_rBitmap, const Graphic& rGraphic, std::list<BitmapEmit>& rBitmaps, ResourceDict& rResourceDict, std::list<StreamRedirect>& rOutputStreams)
9804{
9805 BitmapEx aBitmap( i_rBitmap );
9806 auto ePixelFormat = aBitmap.GetBitmap().getPixelFormat();
9809 BitmapID aID;
9810 aID.m_aPixelSize = aBitmap.GetSizePixel();
9811 aID.m_nSize = vcl::pixelFormatBitCount(ePixelFormat);
9812 aID.m_nChecksum = aBitmap.GetBitmap().GetChecksum();
9813 aID.m_nMaskChecksum = 0;
9814 if( aBitmap.IsAlpha() )
9815 aID.m_nMaskChecksum = aBitmap.GetAlphaMask().GetChecksum();
9816 std::list<BitmapEmit>::const_iterator it = std::find_if(rBitmaps.begin(), rBitmaps.end(),
9817 [&](const BitmapEmit& arg) { return aID == arg.m_aID; });
9818 if (it == rBitmaps.end())
9819 {
9820 rBitmaps.push_front(BitmapEmit());
9821 rBitmaps.front().m_aID = aID;
9822 rBitmaps.front().m_aBitmap = aBitmap;
9824 rBitmaps.front().m_nObject = createObject();
9825 createEmbeddedFile(rGraphic, rBitmaps.front().m_aReferenceXObject, rBitmaps.front().m_nObject);
9826 it = rBitmaps.begin();
9827 }
9828
9829 sal_Int32 nObject = it->m_aReferenceXObject.getObject();
9830 OString aObjName = "Im" + OString::number(nObject);
9831 pushResource(ResourceKind::XObject, aObjName, nObject, rResourceDict, rOutputStreams);
9832
9833 return *it;
9834}
9835
9836const BitmapEmit& PDFWriterImpl::createBitmapEmit( const BitmapEx& i_rBitmap, const Graphic& rGraphic )
9837{
9839}
9840
9841void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap, const Graphic& rGraphic )
9842{
9843 MARK( "drawBitmap (Bitmap)" );
9844
9845 // #i40055# sanity check
9846 if( ! (rDestSize.Width() && rDestSize.Height()) )
9847 return;
9848
9849 const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( rBitmap ), rGraphic );
9850 drawBitmap( rDestPoint, rDestSize, rEmit, COL_TRANSPARENT );
9851}
9852
9853void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEx& rBitmap )
9854{
9855 MARK( "drawBitmap (BitmapEx)" );
9856
9857 // #i40055# sanity check
9858 if( ! (rDestSize.Width() && rDestSize.Height()) )
9859 return;
9860
9861 const BitmapEmit& rEmit = createBitmapEmit( rBitmap, Graphic() );
9862 drawBitmap( rDestPoint, rDestSize, rEmit, COL_TRANSPARENT );
9863}
9864
9865sal_Int32 PDFWriterImpl::createGradient( const Gradient& rGradient, const Size& rSize )
9866{
9867 Size aPtSize( lcl_convert( m_aGraphicsStack.front().m_aMapMode,
9868 MapMode( MapUnit::MapPoint ),
9869 this,
9870 rSize ) );
9871 // check if we already have this gradient
9872 // rounding to point will generally lose some pixels
9873 // round up to point boundary
9874 aPtSize.AdjustWidth( 1 );
9875 aPtSize.AdjustHeight( 1 );
9876 std::list< GradientEmit >::const_iterator it = std::find_if(m_aGradients.begin(), m_aGradients.end(),
9877 [&](const GradientEmit& arg) { return ((rGradient == arg.m_aGradient) && (aPtSize == arg.m_aSize) ); });
9878
9879 if( it == m_aGradients.end() )
9880 {
9881 m_aGradients.push_front( GradientEmit() );
9882 m_aGradients.front().m_aGradient = rGradient;
9883 m_aGradients.front().m_nObject = createObject();
9884 m_aGradients.front().m_aSize = aPtSize;
9885 it = m_aGradients.begin();
9886 }
9887
9888 OStringBuffer aObjName( 16 );
9889 aObjName.append( 'P' );
9890 aObjName.append( it->m_nObject );
9891 pushResource( ResourceKind::Shading, aObjName.makeStringAndClear(), it->m_nObject );
9892
9893 return it->m_nObject;
9894}
9895
9896void PDFWriterImpl::drawGradient( const tools::Rectangle& rRect, const Gradient& rGradient )
9897{
9898 MARK( "drawGradient (Rectangle)" );
9899
9900 sal_Int32 nGradient = createGradient( rGradient, rRect.GetSize() );
9901
9902 Point aTranslate( rRect.BottomLeft() );
9903 aTranslate += Point( 0, 1 );
9904
9906
9907 OStringBuffer aLine( 80 );
9908 aLine.append( "q 1 0 0 1 " );
9909 m_aPages.back().appendPoint( aTranslate, aLine );
9910 aLine.append( " cm " );
9911 // if a stroke is appended reset the clip region before stroke
9912 if( m_aGraphicsStack.front().m_aLineColor != COL_TRANSPARENT )
9913 aLine.append( "q " );
9914 aLine.append( "0 0 " );
9915 m_aPages.back().appendMappedLength( static_cast<sal_Int32>(rRect.GetWidth()), aLine, false );
9916 aLine.append( ' ' );
9917 m_aPages.back().appendMappedLength( static_cast<sal_Int32>(rRect.GetHeight()), aLine );
9918 aLine.append( " re W n\n" );
9919
9920 aLine.append( "/P" );
9921 aLine.append( nGradient );
9922 aLine.append( " sh " );
9923 if( m_aGraphicsStack.front().m_aLineColor != COL_TRANSPARENT )
9924 {
9925 aLine.append( "Q 0 0 " );
9926 m_aPages.back().appendMappedLength( static_cast<sal_Int32>(rRect.GetWidth()), aLine, false );
9927 aLine.append( ' ' );
9928 m_aPages.back().appendMappedLength( static_cast<sal_Int32>(rRect.GetHeight()), aLine );
9929 aLine.append( " re S " );
9930 }
9931 aLine.append( "Q\n" );
9932 writeBuffer( aLine );
9933}
9934
9935void PDFWriterImpl::drawHatch( const tools::PolyPolygon& rPolyPoly, const Hatch& rHatch )
9936{
9937 MARK( "drawHatch" );
9938
9940
9941 if( rPolyPoly.Count() )
9942 {
9943 tools::PolyPolygon aPolyPoly( rPolyPoly );
9944
9945 aPolyPoly.Optimize( PolyOptimizeFlags::NO_SAME );
9947 setLineColor( rHatch.GetColor() );
9948 DrawHatch( aPolyPoly, rHatch, false );
9949 pop();
9950 }
9951}
9952
9954{
9955 MARK( "drawWallpaper" );
9956
9957 bool bDrawColor = false;
9958 bool bDrawGradient = false;
9959 bool bDrawBitmap = false;
9960
9961 BitmapEx aBitmap;
9962 Point aBmpPos = rRect.TopLeft();
9963 Size aBmpSize;
9964 if( rWall.IsBitmap() )
9965 {
9966 aBitmap = rWall.GetBitmap();
9967 aBmpSize = lcl_convert( aBitmap.GetPrefMapMode(),
9968 getMapMode(),
9969 this,
9970 aBitmap.GetPrefSize() );
9971 tools::Rectangle aRect( rRect );
9972 if( rWall.IsRect() )
9973 {
9974 aRect = rWall.GetRect();
9975 aBmpPos = aRect.TopLeft();
9976 aBmpSize = aRect.GetSize();
9977 }
9978 if( rWall.GetStyle() != WallpaperStyle::Scale )
9979 {
9980 if( rWall.GetStyle() != WallpaperStyle::Tile )
9981 {
9982 bDrawBitmap = true;
9983 if( rWall.IsGradient() )
9984 bDrawGradient = true;
9985 else
9986 bDrawColor = true;
9987 switch( rWall.GetStyle() )
9988 {
9990 break;
9992 aBmpPos.AdjustX((aRect.GetWidth()-aBmpSize.Width())/2 );
9993 break;
9995 aBmpPos.AdjustY((aRect.GetHeight()-aBmpSize.Height())/2 );
9996 break;
9998 aBmpPos.AdjustX(aRect.GetWidth()-aBmpSize.Width() );
9999 break;
10001 aBmpPos.AdjustX((aRect.GetWidth()-aBmpSize.Width())/2 );
10002 aBmpPos.AdjustY((aRect.GetHeight()-aBmpSize.Height())/2 );
10003 break;
10005 aBmpPos.AdjustX(aRect.GetWidth()-aBmpSize.Width() );
10006 aBmpPos.AdjustY((aRect.GetHeight()-aBmpSize.Height())/2 );
10007 break;
10009 aBmpPos.AdjustY(aRect.GetHeight()-aBmpSize.Height() );
10010 break;
10012 aBmpPos.AdjustX((aRect.GetWidth()-aBmpSize.Width())/2 );
10013 aBmpPos.AdjustY(aRect.GetHeight()-aBmpSize.Height() );
10014 break;
10016 aBmpPos.AdjustX(aRect.GetWidth()-aBmpSize.Width() );
10017 aBmpPos.AdjustY(aRect.GetHeight()-aBmpSize.Height() );
10018 break;
10019 default: ;
10020 }
10021 }
10022 else
10023 {
10024 // push the bitmap
10025 const BitmapEmit& rEmit = createBitmapEmit( aBitmap, Graphic() );
10026
10027 // convert to page coordinates; this needs to be done here
10028 // since the emit does not know the page anymore
10029 tools::Rectangle aConvertRect( aBmpPos, aBmpSize );
10030 m_aPages.back().convertRect( aConvertRect );
10031
10032 OString aImageName = "Im" + OString::number( rEmit.m_nObject );
10033
10034 // push the pattern
10035 OStringBuffer aTilingStream( 32 );
10036 appendFixedInt( aConvertRect.GetWidth(), aTilingStream );
10037 aTilingStream.append( " 0 0 " );
10038 appendFixedInt( aConvertRect.GetHeight(), aTilingStream );
10039 aTilingStream.append( " 0 0 cm\n/" );
10040 aTilingStream.append( aImageName );
10041 aTilingStream.append( " Do\n" );
10042
10043 m_aTilings.emplace_back( );
10044 m_aTilings.back().m_nObject = createObject();
10045 m_aTilings.back().m_aRectangle = tools::Rectangle( Point( 0, 0 ), aConvertRect.GetSize() );
10046 m_aTilings.back().m_pTilingStream.reset(new SvMemoryStream());
10047 m_aTilings.back().m_pTilingStream->WriteBytes(
10048 aTilingStream.getStr(), aTilingStream.getLength() );
10049 // phase the tiling so wallpaper begins on upper left
10050 if ((aConvertRect.GetWidth() == 0) || (aConvertRect.GetHeight() == 0))
10051 throw o3tl::divide_by_zero();
10052 m_aTilings.back().m_aTransform.matrix[2] = double(aConvertRect.Left() % aConvertRect.GetWidth()) / fDivisor;
10053 m_aTilings.back().m_aTransform.matrix[5] = double(aConvertRect.Top() % aConvertRect.GetHeight()) / fDivisor;
10054 m_aTilings.back().m_aResources.m_aXObjects[aImageName] = rEmit.m_nObject;
10055
10057
10058 OStringBuffer aObjName( 16 );
10059 aObjName.append( 'P' );
10060 aObjName.append( m_aTilings.back().m_nObject );
10061 OString aPatternName( aObjName.makeStringAndClear() );
10062 pushResource( ResourceKind::Pattern, aPatternName, m_aTilings.back().m_nObject );
10063
10064 // fill a rRect with the pattern
10065 OStringBuffer aLine( 100 );
10066 aLine.append( "q /Pattern cs /" );
10067 aLine.append( aPatternName );
10068 aLine.append( " scn " );
10069 m_aPages.back().appendRect( rRect, aLine );
10070 aLine.append( " f Q\n" );
10071 writeBuffer( aLine );
10072 }
10073 }
10074 else
10075 {
10076 aBmpPos = aRect.TopLeft();
10077 aBmpSize = aRect.GetSize();
10078 bDrawBitmap = true;
10079 }
10080
10081 if( aBitmap.IsAlpha() )
10082 {
10083 if( rWall.IsGradient() )
10084 bDrawGradient = true;
10085 else
10086 bDrawColor = true;
10087 }
10088 }
10089 else if( rWall.IsGradient() )
10090 bDrawGradient = true;
10091 else
10092 bDrawColor = true;
10093
10094 if( bDrawGradient )
10095 {
10096 drawGradient( rRect, rWall.GetGradient() );
10097 }
10098 if( bDrawColor )
10099 {
10100 Color aOldLineColor = m_aGraphicsStack.front().m_aLineColor;
10101 Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor;
10103 setFillColor( rWall.GetColor() );
10104 drawRectangle( rRect );
10105 setLineColor( aOldLineColor );
10106 setFillColor( aOldFillColor );
10107 }
10108 if( bDrawBitmap )
10109 {
10110 // set temporary clip region since aBmpPos and aBmpSize
10111 // may be outside rRect
10112 OStringBuffer aLine( 20 );
10113 aLine.append( "q " );
10114 m_aPages.back().appendRect( rRect, aLine );
10115 aLine.append( " W n\n" );
10116 writeBuffer( aLine );
10117 drawBitmap( aBmpPos, aBmpSize, aBitmap );
10118 writeBuffer( "Q\n" );
10119 }
10120}
10121
10123{
10124 OStringBuffer aLine( 256 );
10125 GraphicsState& rNewState = m_aGraphicsStack.front();
10126 // first set clip region since it might invalidate everything else
10127
10128 if( rNewState.m_nUpdateFlags & GraphicsStateUpdateFlags::ClipRegion )
10129 {
10130 rNewState.m_nUpdateFlags &= ~GraphicsStateUpdateFlags::ClipRegion;
10131
10133 ( rNewState.m_bClipRegion && m_aCurrentPDFState.m_aClipRegion != rNewState.m_aClipRegion ) )
10134 {
10136 {
10137 aLine.append( "Q " );
10138 // invalidate everything but the clip region
10140 rNewState.m_nUpdateFlags = ~GraphicsStateUpdateFlags::ClipRegion;
10141 }
10142 if( rNewState.m_bClipRegion )
10143 {
10144 // clip region is always stored in private PDF mapmode
10145 MapMode aNewMapMode = rNewState.m_aMapMode;
10146 rNewState.m_aMapMode = m_aMapMode;
10147 SetMapMode( rNewState.m_aMapMode );
10149
10150 aLine.append("q ");
10151 if ( rNewState.m_aClipRegion.count() )
10152 {
10153 m_aPages.back().appendPolyPolygon( rNewState.m_aClipRegion, aLine );
10154 }
10155 else
10156 {
10157 // tdf#130150 Need to revert tdf#99680, that breaks the
10158 // rule that an set but empty clip-region clips everything
10159 // aka draws nothing -> nothing is in an empty clip-region
10160 aLine.append( "0 0 m h " ); // NULL clip, i.e. nothing visible
10161 }
10162 aLine.append( "W* n\n" );
10163
10164 rNewState.m_aMapMode = aNewMapMode;
10165 SetMapMode( rNewState.m_aMapMode );
10167 }
10168 }
10169 }
10170
10172 {
10174 SetMapMode( rNewState.m_aMapMode );
10175 }
10176
10178 {
10179 rNewState.m_nUpdateFlags &= ~GraphicsStateUpdateFlags::Font;
10180 SetFont( rNewState.m_aFont );
10181 }
10182
10184 {
10185 rNewState.m_nUpdateFlags &= ~GraphicsStateUpdateFlags::LayoutMode;
10186 SetLayoutMode( rNewState.m_nLayoutMode );
10187 }
10188
10189 if( rNewState.m_nUpdateFlags & GraphicsStateUpdateFlags::DigitLanguage )
10190 {
10191 rNewState.m_nUpdateFlags &= ~GraphicsStateUpdateFlags::DigitLanguage;
10193 }
10194
10196 {
10197 rNewState.m_nUpdateFlags &= ~GraphicsStateUpdateFlags::LineColor;
10198 if( m_aCurrentPDFState.m_aLineColor != rNewState.m_aLineColor &&
10199 rNewState.m_aLineColor != COL_TRANSPARENT )
10200 {
10201 appendStrokingColor( rNewState.m_aLineColor, aLine );
10202 aLine.append( "\n" );
10203 }
10204 }
10205
10207 {
10208 rNewState.m_nUpdateFlags &= ~GraphicsStateUpdateFlags::FillColor;
10209 if( m_aCurrentPDFState.m_aFillColor != rNewState.m_aFillColor &&
10210 rNewState.m_aFillColor != COL_TRANSPARENT )
10211 {
10212 appendNonStrokingColor( rNewState.m_aFillColor, aLine );
10213 aLine.append( "\n" );
10214 }
10215 }
10216
10217 if( rNewState.m_nUpdateFlags & GraphicsStateUpdateFlags::TransparentPercent )
10218 {
10219 rNewState.m_nUpdateFlags &= ~GraphicsStateUpdateFlags::TransparentPercent;
10221 {
10222 // TODO: switch extended graphicsstate
10223 }
10224 }
10225
10226 // everything is up to date now
10228 if ((mode != Mode::NOWRITE) && !aLine.isEmpty())
10229 writeBuffer( aLine );
10230}
10231
10232/* #i47544# imitate OutputDevice behaviour:
10233* if a font with a nontransparent color is set, it overwrites the current
10234* text color. OTOH setting the text color will overwrite the color of the font.
10235*/
10237{
10238 Color aColor = rFont.GetColor();
10239 if( aColor == COL_TRANSPARENT )
10240 aColor = m_aGraphicsStack.front().m_aFont.GetColor();
10241 m_aGraphicsStack.front().m_aFont = rFont;
10242 m_aGraphicsStack.front().m_aFont.SetColor( aColor );
10243 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsStateUpdateFlags::Font;
10244}
10245
10247{
10248 OSL_ENSURE( !m_aGraphicsStack.empty(), "invalid graphics stack" );
10249 m_aGraphicsStack.push_front( m_aGraphicsStack.front() );
10250 m_aGraphicsStack.front().m_nFlags = nFlags;
10251}
10252
10254{
10255 OSL_ENSURE( m_aGraphicsStack.size() > 1, "pop without push" );
10256 if( m_aGraphicsStack.size() < 2 )
10257 return;
10258
10259 GraphicsState aState = m_aGraphicsStack.front();
10260 m_aGraphicsStack.pop_front();
10261 GraphicsState& rOld = m_aGraphicsStack.front();
10262
10263 // move those parameters back that were not pushed
10264 // in the first place
10265 if( ! (aState.m_nFlags & PushFlags::LINECOLOR) )
10266 setLineColor( aState.m_aLineColor );
10267 if( ! (aState.m_nFlags & PushFlags::FILLCOLOR) )
10268 setFillColor( aState.m_aFillColor );
10269 if( ! (aState.m_nFlags & PushFlags::FONT) )
10270 setFont( aState.m_aFont );
10271 if( ! (aState.m_nFlags & PushFlags::TEXTCOLOR) )
10272 setTextColor( aState.m_aFont.GetColor() );
10273 if( ! (aState.m_nFlags & PushFlags::MAPMODE) )
10274 setMapMode( aState.m_aMapMode );
10275 if( ! (aState.m_nFlags & PushFlags::CLIPREGION) )
10276 {
10277 // do not use setClipRegion here
10278 // it would convert again assuming the current mapmode
10279 rOld.m_aClipRegion = aState.m_aClipRegion;
10280 rOld.m_bClipRegion = aState.m_bClipRegion;
10281 }
10282 if( ! (aState.m_nFlags & PushFlags::TEXTLINECOLOR ) )
10284 if( ! (aState.m_nFlags & PushFlags::OVERLINECOLOR ) )
10286 if( ! (aState.m_nFlags & PushFlags::TEXTALIGN ) )
10287 setTextAlign( aState.m_aFont.GetAlignment() );
10288 if( ! (aState.m_nFlags & PushFlags::TEXTFILLCOLOR) )
10290 if( ! (aState.m_nFlags & PushFlags::REFPOINT) )
10291 {
10292 // what ?
10293 }
10294 // invalidate graphics state
10295 m_aGraphicsStack.front().m_nUpdateFlags = GraphicsStateUpdateFlags::All;
10296}
10297
10298void PDFWriterImpl::setMapMode( const MapMode& rMapMode )
10299{
10300 m_aGraphicsStack.front().m_aMapMode = rMapMode;
10301 SetMapMode( rMapMode );
10302 m_aCurrentPDFState.m_aMapMode = rMapMode;
10303}
10304
10306{
10307 // tdf#130150 improve coordinate manipulations to double precision transformations
10308 const basegfx::B2DHomMatrix aCurrentTransform(
10310 basegfx::B2DPolyPolygon aRegion(rRegion);
10311
10312 aRegion.transform(aCurrentTransform);
10313 m_aGraphicsStack.front().m_aClipRegion = aRegion;
10314 m_aGraphicsStack.front().m_bClipRegion = true;
10315 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsStateUpdateFlags::ClipRegion;
10316}
10317
10318void PDFWriterImpl::moveClipRegion( sal_Int32 nX, sal_Int32 nY )
10319{
10320 if( !(m_aGraphicsStack.front().m_bClipRegion && m_aGraphicsStack.front().m_aClipRegion.count()) )
10321 return;
10322
10323 // tdf#130150 improve coordinate manipulations to double precision transformations
10324 basegfx::B2DHomMatrix aConvertA;
10325
10326 if(MapUnit::MapPixel == m_aGraphicsStack.front().m_aMapMode.GetMapUnit())
10327 {
10329 }
10330 else
10331 {
10332 aConvertA = LogicToLogic(m_aGraphicsStack.front().m_aMapMode, m_aMapMode);
10333 }
10334
10335 basegfx::B2DPoint aB2DPointA(nX, nY);
10336 basegfx::B2DPoint aB2DPointB(0.0, 0.0);
10337 aB2DPointA *= aConvertA;
10338 aB2DPointB *= aConvertA;
10339 aB2DPointA -= aB2DPointB;
10341
10342 aMat.translate(aB2DPointA.getX(), aB2DPointA.getY());
10343 m_aGraphicsStack.front().m_aClipRegion.transform( aMat );
10344 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsStateUpdateFlags::ClipRegion;
10345}
10346
10348{
10351 intersectClipRegion( aRect );
10352}
10353
10355{
10356 // tdf#130150 improve coordinate manipulations to double precision transformations
10357 const basegfx::B2DHomMatrix aCurrentTransform(
10359 basegfx::B2DPolyPolygon aRegion(rRegion);
10360
10361 aRegion.transform(aCurrentTransform);
10362 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsStateUpdateFlags::ClipRegion;
10363
10364 if( m_aGraphicsStack.front().m_bClipRegion )
10365 {
10367 aRegion = basegfx::utils::prepareForPolygonOperation( aRegion );
10368 m_aGraphicsStack.front().m_aClipRegion = basegfx::utils::solvePolygonOperationAnd( aOld, aRegion );
10369 }
10370 else
10371 {
10372 m_aGraphicsStack.front().m_aClipRegion = aRegion;
10373 m_aGraphicsStack.front().m_bClipRegion = true;
10374 }
10375}
10376
10377void PDFWriterImpl::createNote( const tools::Rectangle& rRect, const PDFNote& rNote, sal_Int32 nPageNr )
10378{
10379 if (nPageNr < 0)
10380 nPageNr = m_nCurrentPage;
10381
10382 if (nPageNr < 0 || o3tl::make_unsigned(nPageNr) >= m_aPages.size())
10383 return;
10384
10385 m_aNotes.emplace_back();
10386 auto & rNoteEntry = m_aNotes.back();
10387 rNoteEntry.m_nObject = createObject();
10388 rNoteEntry.m_aPopUpAnnotation.m_nObject = createObject();
10389 rNoteEntry.m_aPopUpAnnotation.m_nParentObject = rNoteEntry.m_nObject;
10390 rNoteEntry.m_aContents = rNote;
10391 rNoteEntry.m_aRect = rRect;
10392 rNoteEntry.m_nPage = nPageNr;
10393 // convert to default user space now, since the mapmode may change
10394 m_aPages[nPageNr].convertRect(rNoteEntry.m_aRect);
10395
10396 // insert note to page's annotation list
10397 m_aPages[nPageNr].m_aAnnotations.push_back(rNoteEntry.m_nObject);
10398 m_aPages[nPageNr].m_aAnnotations.push_back(rNoteEntry.m_aPopUpAnnotation.m_nObject);
10399}
10400
10401sal_Int32 PDFWriterImpl::createLink(const tools::Rectangle& rRect, sal_Int32 nPageNr, OUString const& rAltText)
10402{
10403 if( nPageNr < 0 )
10404 nPageNr = m_nCurrentPage;
10405
10406 if( nPageNr < 0 || o3tl::make_unsigned(nPageNr) >= m_aPages.size() )
10407 return -1;
10408
10409 sal_Int32 nRet = m_aLinks.size();
10410
10411 m_aLinks.emplace_back(rAltText);
10412 m_aLinks.back().m_nObject = createObject();
10413 m_aLinks.back().m_nPage = nPageNr;
10414 m_aLinks.back().m_aRect = rRect;
10415 // convert to default user space now, since the mapmode may change
10416 m_aPages[nPageNr].convertRect( m_aLinks.back().m_aRect );
10417
10418 // insert link to page's annotation list
10419 m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aLinks.back().m_nObject );
10420
10421 return nRet;
10422}
10423
10424sal_Int32 PDFWriterImpl::createScreen(const tools::Rectangle& rRect, sal_Int32 nPageNr, OUString const& rAltText, OUString const& rMimeType)
10425{
10426 if (nPageNr < 0)
10427 nPageNr = m_nCurrentPage;
10428
10429 if (nPageNr < 0 || o3tl::make_unsigned(nPageNr) >= m_aPages.size())
10430 return -1;
10431
10432 sal_Int32 nRet = m_aScreens.size();
10433
10434 m_aScreens.emplace_back(rAltText, rMimeType);
10435 m_aScreens.back().m_nObject = createObject();
10436 m_aScreens.back().m_nPage = nPageNr;
10437 m_aScreens.back().m_aRect = rRect;
10438 // Convert to default user space now, since the mapmode may change.
10439 m_aPages[nPageNr].convertRect(m_aScreens.back().m_aRect);
10440
10441 // Insert link to page's annotation list.
10442 m_aPages[nPageNr].m_aAnnotations.push_back(m_aScreens.back().m_nObject);
10443
10444 return nRet;
10445}
10446
10447sal_Int32 PDFWriterImpl::createNamedDest( const OUString& sDestName, const tools::Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
10448{
10449 if( nPageNr < 0 )
10450 nPageNr = m_nCurrentPage;
10451
10452 if( nPageNr < 0 || o3tl::make_unsigned(nPageNr) >= m_aPages.size() )
10453 return -1;
10454
10455 sal_Int32 nRet = m_aNamedDests.size();
10456
10457 m_aNamedDests.emplace_back( );
10458 m_aNamedDests.back().m_aDestName = sDestName;
10459 m_aNamedDests.back().m_nPage = nPageNr;
10460 m_aNamedDests.back().m_eType = eType;
10461 m_aNamedDests.back().m_aRect = rRect;
10462 // convert to default user space now, since the mapmode may change
10463 m_aPages[nPageNr].convertRect( m_aNamedDests.back().m_aRect );
10464
10465 return nRet;
10466}
10467
10468sal_Int32 PDFWriterImpl::createDest( const tools::Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
10469{
10470 if( nPageNr < 0 )
10471 nPageNr = m_nCurrentPage;
10472
10473 if( nPageNr < 0 || o3tl::make_unsigned(nPageNr) >= m_aPages.size() )
10474 return -1;
10475
10476 sal_Int32 nRet = m_aDests.size();
10477
10478 m_aDests.emplace_back( );
10479 m_aDests.back().m_nPage = nPageNr;
10480 m_aDests.back().m_eType = eType;
10481 m_aDests.back().m_aRect = rRect;
10482 // convert to default user space now, since the mapmode may change
10483 m_aPages[nPageNr].convertRect( m_aDests.back().m_aRect );
10484
10485 return nRet;
10486}
10487
10488sal_Int32 PDFWriterImpl::registerDestReference( sal_Int32 nDestId, const tools::Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
10489{
10490 m_aDestinationIdTranslation[ nDestId ] = createDest( rRect, nPageNr, eType );
10491 return m_aDestinationIdTranslation[ nDestId ];
10492}
10493
10494void PDFWriterImpl::setLinkDest( sal_Int32 nLinkId, sal_Int32 nDestId )
10495{
10496 if( nLinkId < 0 || o3tl::make_unsigned(nLinkId) >= m_aLinks.size() )
10497 return;
10498 if( nDestId < 0 || o3tl::make_unsigned(nDestId) >= m_aDests.size() )
10499 return;
10500
10501 m_aLinks[ nLinkId ].m_nDest = nDestId;
10502}
10503
10504void PDFWriterImpl::setLinkURL( sal_Int32 nLinkId, const OUString& rURL )
10505{
10506 if( nLinkId < 0 || o3tl::make_unsigned(nLinkId) >= m_aLinks.size() )
10507 return;
10508
10509 m_aLinks[ nLinkId ].m_nDest = -1;
10510
10511 using namespace ::com::sun::star;
10512
10513 if (!m_xTrans.is())
10514 {
10515 uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
10516 m_xTrans = util::URLTransformer::create(xContext);
10517 }
10518
10519 util::URL aURL;
10520 aURL.Complete = rURL;
10521
10522 m_xTrans->parseStrict( aURL );
10523
10524 m_aLinks[ nLinkId ].m_aURL = aURL.Complete;
10525}
10526
10527void PDFWriterImpl::setScreenURL(sal_Int32 nScreenId, const OUString& rURL)
10528{
10529 if (nScreenId < 0 || o3tl::make_unsigned(nScreenId) >= m_aScreens.size())
10530 return;
10531
10532 m_aScreens[nScreenId].m_aURL = rURL;
10533}
10534
10535void PDFWriterImpl::setScreenStream(sal_Int32 nScreenId, const OUString& rURL)
10536{
10537 if (nScreenId < 0 || o3tl::make_unsigned(nScreenId) >= m_aScreens.size())
10538 return;
10539
10540 m_aScreens[nScreenId].m_aTempFileURL = rURL;
10541 m_aScreens[nScreenId].m_nTempFileObject = createObject();
10542}
10543
10544void PDFWriterImpl::setLinkPropertyId( sal_Int32 nLinkId, sal_Int32 nPropertyId )
10545{
10546 m_aLinkPropertyMap[ nPropertyId ] = nLinkId;
10547}
10548
10549sal_Int32 PDFWriterImpl::createOutlineItem( sal_Int32 nParent, std::u16string_view rText, sal_Int32 nDestID )
10550{
10551 // create new item
10552 sal_Int32 nNewItem = m_aOutline.size();
10553 m_aOutline.emplace_back( );
10554
10555 // set item attributes
10556 setOutlineItemParent( nNewItem, nParent );
10557 setOutlineItemText( nNewItem, rText );
10558 setOutlineItemDest( nNewItem, nDestID );
10559
10560 return nNewItem;
10561}
10562
10563void PDFWriterImpl::setOutlineItemParent( sal_Int32 nItem, sal_Int32 nNewParent )
10564{
10565 if( nItem < 1 || o3tl::make_unsigned(nItem) >= m_aOutline.size() )
10566 return;
10567
10568 if( nNewParent < 0 || o3tl::make_unsigned(nNewParent) >= m_aOutline.size() || nNewParent == nItem )
10569 {
10570 nNewParent = 0;
10571 }
10572 // insert item to new parent's list of children
10573 m_aOutline[ nNewParent ].m_aChildren.push_back( nItem );
10574}
10575
10576void PDFWriterImpl::setOutlineItemText( sal_Int32 nItem, std::u16string_view rText )
10577{
10578 if( nItem < 1 || o3tl::make_unsigned(nItem) >= m_aOutline.size() )
10579 return;
10580
10581 m_aOutline[ nItem ].m_aTitle = psp::WhitespaceToSpace( rText );
10582}
10583
10584void PDFWriterImpl::setOutlineItemDest( sal_Int32 nItem, sal_Int32 nDestID )
10585{
10586 if( nItem < 1 || o3tl::make_unsigned(nItem) >= m_aOutline.size() ) // item does not exist
10587 return;
10588 if( nDestID < 0 || o3tl::make_unsigned(nDestID) >= m_aDests.size() ) // dest does not exist
10589 return;
10590 m_aOutline[nItem].m_nDestID = nDestID;
10591}
10592
10594{
10595 static constexpr auto aTagStrings = frozen::make_map<PDFWriter::StructElement, const char*>({
10596 { PDFWriter::NonStructElement, "NonStruct" },
10597 { PDFWriter::Document, "Document" },
10598 { PDFWriter::Part, "Part" },
10599 { PDFWriter::Article, "Art" },
10600 { PDFWriter::Section, "Sect" },
10601 { PDFWriter::Division, "Div" },
10602 { PDFWriter::BlockQuote, "BlockQuote" },
10603 { PDFWriter::Caption, "Caption" },
10604 { PDFWriter::TOC, "TOC" },
10605 { PDFWriter::TOCI, "TOCI" },
10606 { PDFWriter::Index, "Index" },
10607 { PDFWriter::Paragraph, "P" },
10608 { PDFWriter::Heading, "H" },
10609 { PDFWriter::H1, "H1" },
10610 { PDFWriter::H2, "H2" },
10611 { PDFWriter::H3, "H3" },
10612 { PDFWriter::H4, "H4" },
10613 { PDFWriter::H5, "H5" },
10614 { PDFWriter::H6, "H6" },
10615 { PDFWriter::List, "L" },
10616 { PDFWriter::ListItem, "LI" },
10617 { PDFWriter::LILabel, "Lbl" },
10618 { PDFWriter::LIBody, "LBody" },
10619 { PDFWriter::Table, "Table" },
10620 { PDFWriter::TableRow, "TR" },
10621 { PDFWriter::TableHeader, "TH" },
10622 { PDFWriter::TableData, "TD" },
10623 { PDFWriter::Span, "Span" },
10624 { PDFWriter::Quote, "Quote" },
10625 { PDFWriter::Note, "Note" },
10626 { PDFWriter::Reference, "Reference" },
10627 { PDFWriter::BibEntry, "BibEntry" },
10628 { PDFWriter::Code, "Code" },
10629 { PDFWriter::Link, "Link" },
10630 { PDFWriter::Annot, "Annot" },
10631 { PDFWriter::Figure, "Figure" },
10632 { PDFWriter::Formula, "Formula"},
10633 { PDFWriter::Form, "Form" }
10634 });
10635
10636 if (eType == PDFWriter::Annot
10638 {
10639 return "Figure"; // fallback
10640 }
10641
10642 auto it = aTagStrings.find( eType );
10643
10644 return it != aTagStrings.end() ? it->second : "Div";
10645}
10646
10648{
10649 OString aTag = getStructureTag(eType);
10650 // For PDF/UA it's not allowed to map an alias with the same name.
10651 // Not aware of a reason for doing it in any case, so just don't do it.
10652 if (aAlias != aTag)
10653 m_aRoleMap[aAlias] = aTag;
10654}
10655
10657{
10659 if( m_bEmitStructure &&
10660 m_nCurrentStructElement > 0 && // StructTreeRoot
10661 // Document = SwPageFrame => this is not *inside* the page content
10662 // stream so do not emit MCID!
10665 ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence
10666 )
10667 {
10669 OStringBuffer aLine( 128 );
10670 sal_Int32 nMCID = m_aPages[ m_nCurrentPage ].m_aMCIDParents.size();
10671 aLine.append( "/" );
10672 if( !rEle.m_aAlias.isEmpty() )
10673 aLine.append( rEle.m_aAlias );
10674 else
10675 aLine.append( getStructureTag(*rEle.m_oType) );
10676 aLine.append( "<</MCID " );
10677 aLine.append( nMCID );
10678 aLine.append( ">>BDC\n" );
10679 writeBuffer( aLine );
10680
10681 // update the element's content list
10682 SAL_INFO("vcl.pdfwriter", "beginning marked content id " << nMCID << " on page object "
10683 << m_aPages[ m_nCurrentPage ].m_nPageObject << ", structure first page = "
10684 << rEle.m_nFirstPageObject);
10685 rEle.m_aKids.emplace_back( nMCID, m_aPages[m_nCurrentPage].m_nPageObject );
10686 // update the page's mcid parent list
10687 m_aPages[ m_nCurrentPage ].m_aMCIDParents.push_back( rEle.m_nObject );
10688 // mark element MC sequence as open
10689 rEle.m_bOpenMCSeq = true;
10690 }
10691 // handle artifacts
10692 else if( ! m_bEmitStructure && m_aContext.Tagged &&
10696 ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence
10697 )
10698 {
10699 OString aLine = "/Artifact ";
10700 writeBuffer( aLine );
10701 // emit property list if requested
10702 OStringBuffer buf;
10703 for (auto const& rAttr : m_aStructure[m_nCurrentStructElement].m_aAttributes)
10704 {
10705 appendStructureAttributeLine(rAttr.first, rAttr.second, buf, false);
10706 }
10707 if (buf.isEmpty())
10708 {
10709 writeBuffer("BMC\n");
10710 }
10711 else
10712 {
10713 writeBuffer("<<");
10714 writeBuffer(buf);
10715 writeBuffer(">> BDC\n");
10716 }
10717 // mark element MC sequence as open
10718 m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = true;
10719 }
10720}
10721
10723{
10724 if (m_nCurrentStructElement > 0 // not StructTreeRoot
10727 || (endMode != EndMode::OnlyStruct
10729 && m_aStructure[m_nCurrentStructElement].m_bOpenMCSeq)
10730 {
10731 writeBuffer( "EMC\n" );
10732 m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = false;
10733 }
10734}
10735
10737{
10738 bool bEmit = false;
10739 if( m_aContext.Tagged )
10740 {
10741 bEmit = true;
10742 sal_Int32 nEle = m_nCurrentStructElement;
10743 while( nEle > 0 && o3tl::make_unsigned(nEle) < m_aStructure.size() )
10744 {
10745 if (m_aStructure[nEle].m_oType
10746 && *m_aStructure[nEle].m_oType == PDFWriter::NonStructElement)
10747 {
10748 bEmit = false;
10749 break;
10750 }
10751 nEle = m_aStructure[ nEle ].m_nParentElement;
10752 }
10753 }
10754 return bEmit;
10755}
10756
10758{
10759 if( ! m_aContext.Tagged )
10760 return -1;
10761
10762 sal_Int32 nNewId = sal_Int32(m_aStructure.size());
10763 m_aStructure.emplace_back();
10764 PDFStructureElement& rEle = m_aStructure.back();
10765 // leave rEle.m_oType uninitialised
10766 rEle.m_nOwnElement = nNewId;
10767 // temporary parent
10769 rEle.m_nFirstPageObject = m_aPages[ m_nCurrentPage ].m_nPageObject;
10770 m_aStructure[ m_nCurrentStructElement ].m_aChildren.push_back( nNewId );
10771 return nNewId;
10772}
10773
10775 PDFWriter::StructElement const eType, std::u16string_view const rAlias)
10776{
10777 if( ! m_aContext.Tagged )
10778 return;
10779
10780 if( m_nCurrentStructElement == 0 &&
10782 {
10783 // struct tree root hit, but not beginning document
10784 // this might happen with setCurrentStructureElement
10785 // silently insert structure into document again if one properly exists
10786 if( ! m_aStructure[ 0 ].m_aChildren.empty() )
10787 {
10788 const std::vector< sal_Int32 >& rRootChildren = m_aStructure[0].m_aChildren;
10789 auto it = std::find_if(rRootChildren.begin(), rRootChildren.end(),
10790 [&](sal_Int32 nElement) {
10791 return m_aStructure[nElement].m_oType
10792 && *m_aStructure[nElement].m_oType == PDFWriter::Document; });
10793 if( it != rRootChildren.end() )
10794 {
10796 SAL_WARN( "vcl.pdfwriter", "Structure element inserted to StructTreeRoot that is not a document" );
10797 }
10798 else {
10799 OSL_FAIL( "document structure in disorder !" );
10800 }
10801 }
10802 else {
10803 OSL_FAIL( "PDF document structure MUST be contained in a Document element" );
10804 }
10805 }
10806
10808 assert(!rEle.m_oType);
10809 rEle.m_oType.emplace(eType);
10810 // remove it from its possibly placeholder parent; append to real parent
10811 auto const it(std::find(m_aStructure[rEle.m_nParentElement].m_aChildren.begin(),
10812 m_aStructure[rEle.m_nParentElement].m_aChildren.end(), id));
10813 assert(it != m_aStructure[rEle.m_nParentElement].m_aChildren.end());
10814 m_aStructure[rEle.m_nParentElement].m_aChildren.erase(it);
10816 rEle.m_nFirstPageObject = m_aPages[ m_nCurrentPage ].m_nPageObject;
10817 m_aStructure[m_nCurrentStructElement].m_aChildren.push_back(id);
10818
10819 // handle alias names
10820 if( !rAlias.empty() && eType != PDFWriter::NonStructElement )
10821 {
10822 OStringBuffer aNameBuf( rAlias.size() );
10823 appendName( rAlias, aNameBuf );
10824 OString aAliasName( aNameBuf.makeStringAndClear() );
10825 rEle.m_aAlias = aAliasName;
10826 addRoleMap(aAliasName, eType);
10827 }
10828
10829 if (m_bEmitStructure && eType != PDFWriter::NonStructElement) // don't create nonexistent objects
10830 {
10831 rEle.m_nObject = createObject();
10832 // update parent's kids list
10833 m_aStructure[ rEle.m_nParentElement ].m_aKids.emplace_back(rEle.m_nObject);
10834 // ISO 14289-1:2014, Clause: 7.9
10835 if (*rEle.m_oType == PDFWriter::Note)
10836 {
10837 m_StructElemObjsWithID.insert(rEle.m_nObject);
10838 }
10839 }
10840}
10841
10843{
10844 if( m_nCurrentPage < 0 )
10845 return;
10846
10847 if( ! m_aContext.Tagged )
10848 return;
10849
10850 assert(id != -1 && "cid#1538888 doesn't consider above m_aContext.Tagged");
10851
10852 // close eventual current MC sequence
10854
10858
10860 {
10861 OStringBuffer aLine( "beginStructureElement " );
10862 aLine.append( m_nCurrentStructElement );
10863 aLine.append( ": " );
10864 aLine.append( rEle.m_oType
10865 ? getStructureTag(*rEle.m_oType)
10866 : "<placeholder>" );
10867 if( !rEle.m_aAlias.isEmpty() )
10868 {
10869 aLine.append( " aliased as \"" );
10870 aLine.append( rEle.m_aAlias );
10871 aLine.append( '\"' );
10872 }
10873 emitComment( aLine.getStr() );
10874 }
10875
10876 // check whether to emit structure henceforth
10878}
10879
10881{
10882 if( m_nCurrentPage < 0 )
10883 return;
10884
10885 if( ! m_aContext.Tagged )
10886 return;
10887
10888 if( m_nCurrentStructElement == 0 )
10889 {
10890 // hit the struct tree root, that means there is an endStructureElement
10891 // without corresponding beginStructureElement
10892 return;
10893 }
10894
10895 // end the marked content sequence
10897
10898 OStringBuffer aLine;
10900 {
10901 aLine.append( "endStructureElement " );
10902 aLine.append( m_nCurrentStructElement );
10903 aLine.append( ": " );
10904 aLine.append( m_aStructure[m_nCurrentStructElement].m_oType
10906 : "<placeholder>" );
10907 if( !m_aStructure[ m_nCurrentStructElement ].m_aAlias.isEmpty() )
10908 {
10909 aLine.append( " aliased as \"" );
10910 aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias );
10911 aLine.append( '\"' );
10912 }
10913 }
10914
10915 // "end" the structure element, the parent becomes current element
10918
10919 // check whether to emit structure henceforth
10921
10923 {
10924 emitComment( aLine.getStr() );
10925 }
10926}
10927
10928namespace {
10929
10930void removePlaceholderSEImpl(std::vector<PDFStructureElement> & rStructure,
10931 std::vector<sal_Int32>::iterator & rParentIt)
10932{
10933 PDFStructureElement& rEle(rStructure[*rParentIt]);
10934 removePlaceholderSE(rStructure, rEle);
10935
10936 if (!rEle.m_oType)
10937 {
10938 // Placeholder was not initialised - should not happen when printing
10939 // a full page, but might if a selection is printed, which can be only
10940 // a shape without its anchor.
10941 // Handle this by moving the children to the parent SE.
10942 PDFStructureElement & rParent(rStructure[rEle.m_nParentElement]);
10943 rParentIt = rParent.m_aChildren.erase(rParentIt);
10944 std::vector<sal_Int32> children;
10945 for (auto const child : rEle.m_aChildren)
10946 {
10947 PDFStructureElement& rChild = rStructure[child];
10948 rChild.m_nParentElement = rEle.m_nParentElement;
10949 children.push_back(rChild.m_nOwnElement);
10950 }
10951 rParentIt = rParent.m_aChildren.insert(rParentIt, children.begin(), children.end())
10952 + children.size();
10953 }
10954 else
10955 {
10956 ++rParentIt;
10957 }
10958
10959}
10960
10961void removePlaceholderSE(std::vector<PDFStructureElement> & rStructure, PDFStructureElement& rEle)
10962{
10963 for (auto it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); )
10964 {
10965 removePlaceholderSEImpl(rStructure, it);
10966 }
10967}
10968
10969} // end anonymous namespace
10970
10971/*
10972 * This function adds an internal structure list container to overcome the 8191 elements array limitation
10973 * in kids element emission.
10974 * Recursive function
10975 *
10976 */
10978{
10979 if (rEle.m_nOwnElement != rEle.m_nParentElement
10981 {
10982 return;
10983 }
10984
10985 for (auto const& child : rEle.m_aChildren)
10986 {
10987 assert(child > 0 && o3tl::make_unsigned(child) < m_aStructure.size());
10988 if( child > 0 && o3tl::make_unsigned(child) < m_aStructure.size() )
10989 {
10990 PDFStructureElement& rChild = m_aStructure[ child ];
10991 if (*rChild.m_oType != PDFWriter::NonStructElement)
10992 {
10993 //triggered when a child of the rEle element is found
10994 assert(rChild.m_nParentElement == rEle.m_nOwnElement);
10995 if( rChild.m_nParentElement == rEle.m_nOwnElement )
10996 addInternalStructureContainer( rChild );//examine the child
10997 else
10998 {
10999 OSL_FAIL( "PDFWriterImpl::addInternalStructureContainer: invalid child structure element" );
11000 SAL_INFO("vcl.pdfwriter", "PDFWriterImpl::addInternalStructureContainer: invalid child structure element with id " << child );
11001 }
11002 }
11003 }
11004 else
11005 {
11006 OSL_FAIL( "PDFWriterImpl::emitStructure: invalid child structure id" );
11007 SAL_INFO("vcl.pdfwriter", "PDFWriterImpl::addInternalStructureContainer: invalid child structure id " << child );
11008 }
11009 }
11010
11011 if( rEle.m_nOwnElement == rEle.m_nParentElement )
11012 return;
11013
11014 if( rEle.m_aKids.empty() )
11015 return;
11016
11017 if( rEle.m_aKids.size() <= ncMaxPDFArraySize ) return;
11018
11019 //then we need to add the containers for the kids elements
11020 // a list to be used for the new kid element
11021 std::list< PDFStructureElementKid > aNewKids;
11022 std::vector< sal_Int32 > aNewChildren;
11023
11024 // add Div in RoleMap, in case no one else did (TODO: is it needed? Is it dangerous?)
11025 OString aAliasName("Div");
11026 addRoleMap(aAliasName, PDFWriter::Division);
11027
11028 while( rEle.m_aKids.size() > ncMaxPDFArraySize )
11029 {
11030 sal_Int32 nCurrentStructElement = rEle.m_nOwnElement;
11031 sal_Int32 nNewId = sal_Int32(m_aStructure.size());
11032 m_aStructure.emplace_back( );
11033 PDFStructureElement& rEleNew = m_aStructure.back();
11034 rEleNew.m_aAlias = aAliasName;
11035 rEleNew.m_oType.emplace(PDFWriter::Division); // a new Div type container
11036 rEleNew.m_nOwnElement = nNewId;
11037 rEleNew.m_nParentElement = nCurrentStructElement;
11038 //inherit the same page as the first child to be reparented
11039 rEleNew.m_nFirstPageObject = m_aStructure[ rEle.m_aChildren.front() ].m_nFirstPageObject;
11040 rEleNew.m_nObject = createObject();//assign a PDF object number
11041 //add the object to the kid list of the parent
11042 aNewKids.emplace_back( rEleNew.m_nObject );
11043 aNewChildren.push_back( nNewId );
11044
11045 std::vector< sal_Int32 >::iterator aChildEndIt( rEle.m_aChildren.begin() );
11046 std::list< PDFStructureElementKid >::iterator aKidEndIt( rEle.m_aKids.begin() );
11047 advance( aChildEndIt, ncMaxPDFArraySize );
11048 advance( aKidEndIt, ncMaxPDFArraySize );
11049
11050 rEleNew.m_aKids.splice( rEleNew.m_aKids.begin(),
11051 rEle.m_aKids,
11052 rEle.m_aKids.begin(),
11053 aKidEndIt );
11054 rEleNew.m_aChildren.insert( rEleNew.m_aChildren.begin(),
11055 rEle.m_aChildren.begin(),
11056 aChildEndIt );
11057 rEle.m_aChildren.erase( rEle.m_aChildren.begin(), aChildEndIt );
11058
11059 // set the kid's new parent
11060 for (auto const& child : rEleNew.m_aChildren)
11061 {
11062 m_aStructure[ child ].m_nParentElement = nNewId;
11063 }
11064 }
11065 //finally add the new kids resulting from the container added
11066 rEle.m_aKids.insert( rEle.m_aKids.begin(), aNewKids.begin(), aNewKids.end() );
11067 rEle.m_aChildren.insert( rEle.m_aChildren.begin(), aNewChildren.begin(), aNewChildren.end() );
11068}
11069
11071{
11072 bool bSuccess = false;
11073
11074 if( m_aContext.Tagged && nEle >= 0 && o3tl::make_unsigned(nEle) < m_aStructure.size() )
11075 {
11076 // end eventual previous marked content sequence
11078
11082 {
11083 OStringBuffer aLine( "setCurrentStructureElement " );
11084 aLine.append( m_nCurrentStructElement );
11085 aLine.append( ": " );
11086 aLine.append( m_aStructure[m_nCurrentStructElement].m_oType
11088 : "<placeholder>" );
11089 if( !m_aStructure[ m_nCurrentStructElement ].m_aAlias.isEmpty() )
11090 {
11091 aLine.append( " aliased as \"" );
11092 aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias );
11093 aLine.append( '\"' );
11094 }
11095 if( ! m_bEmitStructure )
11096 aLine.append( " (inside NonStruct)" );
11097 emitComment( aLine.getStr() );
11098 }
11099 bSuccess = true;
11100 }
11101
11102 return bSuccess;
11103}
11104
11106{
11107 if( !m_aContext.Tagged )
11108 return false;
11109
11110 assert(m_aStructure[m_nCurrentStructElement].m_oType);
11111 bool bInsert = false;
11114 // allow it for topmost non-structured element
11115 || (m_aContext.Tagged
11116 && (0 == m_aStructure[m_nCurrentStructElement].m_nParentElement
11117 || !m_aStructure[m_aStructure[m_nCurrentStructElement].m_nParentElement].m_oType
11119 {
11121 switch( eAttr )
11122 {
11124 if( eVal == PDFWriter::Block ||
11125 eVal == PDFWriter::Inline ||
11126 eVal == PDFWriter::Before ||
11127 eVal == PDFWriter::Start ||
11128 eVal == PDFWriter::End )
11129 bInsert = true;
11130 break;
11132 if( eVal == PDFWriter::LrTb ||
11133 eVal == PDFWriter::RlTb ||
11134 eVal == PDFWriter::TbRl )
11135 {
11136 bInsert = true;
11137 }
11138 break;
11140 if( eVal == PDFWriter::Start ||
11141 eVal == PDFWriter::Center ||
11142 eVal == PDFWriter::End ||
11143 eVal == PDFWriter::Justify )
11144 {
11145 if( eType == PDFWriter::Paragraph ||
11147 eType == PDFWriter::H1 ||
11148 eType == PDFWriter::H2 ||
11149 eType == PDFWriter::H3 ||
11150 eType == PDFWriter::H4 ||
11151 eType == PDFWriter::H5 ||
11152 eType == PDFWriter::H6 ||
11161 {
11162 bInsert = true;
11163 }
11164 }
11165 break;
11166 case PDFWriter::Width:
11167 case PDFWriter::Height:
11168 if( eVal == PDFWriter::Auto )
11169 {
11170 if( eType == PDFWriter::Figure ||
11176 {
11177 bInsert = true;
11178 }
11179 }
11180 break;
11182 if( eVal == PDFWriter::Before ||
11183 eVal == PDFWriter::Middle ||
11184 eVal == PDFWriter::After ||
11185 eVal == PDFWriter::Justify )
11186 {
11189 {
11190 bInsert = true;
11191 }
11192 }
11193 break;
11195 if( eVal == PDFWriter::Start ||
11196 eVal == PDFWriter::Center ||
11197 eVal == PDFWriter::End )
11198 {
11201 {
11202 bInsert = true;
11203 }
11204 }
11205 break;
11207 if( eVal == PDFWriter::Normal ||
11208 eVal == PDFWriter::Auto )
11209 {
11210 // only for ILSE and BLSE
11211 if( eType == PDFWriter::Paragraph ||
11213 eType == PDFWriter::H1 ||
11214 eType == PDFWriter::H2 ||
11215 eType == PDFWriter::H3 ||
11216 eType == PDFWriter::H4 ||
11217 eType == PDFWriter::H5 ||
11218 eType == PDFWriter::H6 ||
11234 {
11235 bInsert = true;
11236 }
11237 }
11238 break;
11240 if( eVal == PDFWriter::NONE ||
11241 eVal == PDFWriter::Underline ||
11242 eVal == PDFWriter::Overline ||
11243 eVal == PDFWriter::LineThrough )
11244 {
11245 // only for ILSE and BLSE
11246 if( eType == PDFWriter::Paragraph ||
11248 eType == PDFWriter::H1 ||
11249 eType == PDFWriter::H2 ||
11250 eType == PDFWriter::H3 ||
11251 eType == PDFWriter::H4 ||
11252 eType == PDFWriter::H5 ||
11253 eType == PDFWriter::H6 ||
11269 {
11270 bInsert = true;
11271 }
11272 }
11273 break;
11274 case PDFWriter::Scope:
11275 if (eVal == PDFWriter::Row || eVal == PDFWriter::Column || eVal == PDFWriter::Both)
11276 {
11279 {
11280 bInsert = true;
11281 }
11282 }
11283 break;
11284 case PDFWriter::Type:
11285 if (eVal == PDFWriter::Pagination || eVal == PDFWriter::Layout || eVal == PDFWriter::Page)
11286 // + Background for PDF >= 1.7
11287 {
11289 {
11290 bInsert = true;
11291 }
11292 }
11293 break;
11294 case PDFWriter::Subtype:
11295 if (eVal == PDFWriter::Header || eVal == PDFWriter::Footer || eVal == PDFWriter::Watermark)
11296 {
11299 {
11300 bInsert = true;
11301 }
11302 }
11303 break;
11304 case PDFWriter::Role:
11305 if (eVal == PDFWriter::Rb || eVal == PDFWriter::Cb || eVal == PDFWriter::Pb || eVal == PDFWriter::Tv)
11306 {
11307 if (eType == PDFWriter::Form
11309 {
11310 bInsert = true;
11311 }
11312 }
11313 break;
11315 if( eVal == PDFWriter::NONE ||
11316 eVal == PDFWriter::Disc ||
11317 eVal == PDFWriter::Circle ||
11318 eVal == PDFWriter::Square ||
11319 eVal == PDFWriter::Decimal ||
11320 eVal == PDFWriter::UpperRoman ||
11321 eVal == PDFWriter::LowerRoman ||
11322 eVal == PDFWriter::UpperAlpha ||
11323 eVal == PDFWriter::LowerAlpha )
11324 {
11325 if( eType == PDFWriter::List )
11326 bInsert = true;
11327 }
11328 break;
11329 default: break;
11330 }
11331 }
11332
11333 if( bInsert )
11334 m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( eVal );
11336 SAL_INFO("vcl.pdfwriter",
11337 "rejecting setStructureAttribute( " << getAttributeTag( eAttr )
11338 << ", " << getAttributeValueTag( eVal )
11339 << " ) on " << getStructureTag(*m_aStructure[m_nCurrentStructElement].m_oType)
11340 << " (" << m_aStructure[ m_nCurrentStructElement ].m_aAlias
11341 << ") element");
11342
11343 return bInsert;
11344}
11345
11347{
11348 if( ! m_aContext.Tagged )
11349 return false;
11350
11351 assert(m_aStructure[m_nCurrentStructElement].m_oType);
11352 bool bInsert = false;
11354 {
11355 if( eAttr == PDFWriter::Language )
11356 {
11358 return true;
11359 }
11360
11362 switch( eAttr )
11363 {
11368 // just for BLSE
11369 if( eType == PDFWriter::Paragraph ||
11371 eType == PDFWriter::H1 ||
11372 eType == PDFWriter::H2 ||
11373 eType == PDFWriter::H3 ||
11374 eType == PDFWriter::H4 ||
11375 eType == PDFWriter::H5 ||
11376 eType == PDFWriter::H6 ||
11385 {
11386 bInsert = true;
11387 }
11388 break;
11390 // paragraph like BLSE and additional elements
11391 if( eType == PDFWriter::Paragraph ||
11393 eType == PDFWriter::H1 ||
11394 eType == PDFWriter::H2 ||
11395 eType == PDFWriter::H3 ||
11396 eType == PDFWriter::H4 ||
11397 eType == PDFWriter::H5 ||
11398 eType == PDFWriter::H6 ||
11403 {
11404 bInsert = true;
11405 }
11406 break;
11407 case PDFWriter::Width:
11408 case PDFWriter::Height:
11409 if( eType == PDFWriter::Figure ||
11415 {
11416 bInsert = true;
11417 }
11418 break;
11421 // only for ILSE and BLSE
11422 if( eType == PDFWriter::Paragraph ||
11424 eType == PDFWriter::H1 ||
11425 eType == PDFWriter::H2 ||
11426 eType == PDFWriter::H3 ||
11427 eType == PDFWriter::H4 ||
11428 eType == PDFWriter::H5 ||
11429 eType == PDFWriter::H6 ||
11445 {
11446 bInsert = true;
11447 }
11448 break;
11449 case PDFWriter::RowSpan:
11450 case PDFWriter::ColSpan:
11451 // only for table cells
11454 {
11455 bInsert = true;
11456 }
11457 break;
11459 if( eType == PDFWriter::Link )
11460 bInsert = true;
11461 break;
11462 default: break;
11463 }
11464 }
11465
11466 if( bInsert )
11469 SAL_INFO("vcl.pdfwriter",
11470 "rejecting setStructureAttributeNumerical( " << getAttributeTag( eAttr )
11471 << ", " << static_cast<int>(nValue)
11472 << " ) on " << getStructureTag(*m_aStructure[m_nCurrentStructElement].m_oType)
11473 << " (" << m_aStructure[ m_nCurrentStructElement ].m_aAlias
11474 << ") element");
11475
11476 return bInsert;
11477}
11478
11480{
11481 sal_Int32 nPageNr = m_nCurrentPage;
11482 if( nPageNr < 0 || o3tl::make_unsigned(nPageNr) >= m_aPages.size() || !m_aContext.Tagged )
11483 return;
11484
11486 return;
11487
11488 assert(m_aStructure[m_nCurrentStructElement].m_oType);
11490 if( eType == PDFWriter::Figure ||
11495 {
11496 m_aStructure[ m_nCurrentStructElement ].m_aBBox = rRect;
11497 // convert to default user space now, since the mapmode may change
11498 m_aPages[nPageNr].convertRect( m_aStructure[ m_nCurrentStructElement ].m_aBBox );
11499 }
11500}
11501
11502void PDFWriterImpl::setStructureAnnotIds(::std::vector<sal_Int32> const& rAnnotIds)
11503{
11504 assert(!(m_nCurrentPage < 0 || m_aPages.size() <= o3tl::make_unsigned(m_nCurrentPage)));
11505
11507 {
11508 return;
11509 }
11510
11511 m_aStructure[m_nCurrentStructElement].m_AnnotIds = rAnnotIds;
11512}
11513
11514void PDFWriterImpl::setActualText( const OUString& rText )
11515{
11517 {
11518 m_aStructure[ m_nCurrentStructElement ].m_aActualText = rText;
11519 }
11520}
11521
11522void PDFWriterImpl::setAlternateText( const OUString& rText )
11523{
11525 {
11526 m_aStructure[ m_nCurrentStructElement ].m_aAltText = rText;
11527 }
11528}
11529
11530void PDFWriterImpl::setPageTransition( PDFWriter::PageTransition eType, sal_uInt32 nMilliSec, sal_Int32 nPageNr )
11531{
11532 if( nPageNr < 0 )
11533 nPageNr = m_nCurrentPage;
11534
11535 if( nPageNr < 0 || o3tl::make_unsigned(nPageNr) >= m_aPages.size() )
11536 return;
11537
11538 m_aPages[ nPageNr ].m_eTransition = eType;
11539 m_aPages[ nPageNr ].m_nTransTime = nMilliSec;
11540}
11541
11543{
11544 // loop over radio groups
11545 for (auto const& group : m_aRadioGroupWidgets)
11546 {
11547 PDFWidget& rGroupWidget = m_aWidgets[ group.second ];
11548 // check whether all kids have a unique OnValue
11549 std::unordered_map< OUString, sal_Int32 > aOnValues;
11550 bool bIsUnique = true;
11551 for (auto const& nKidIndex : rGroupWidget.m_aKidsIndex)
11552 {
11553 const OUString& rVal = m_aWidgets[nKidIndex].m_aOnValue;
11554 SAL_INFO("vcl.pdfwriter", "OnValue: " << rVal);
11555 if( aOnValues.find( rVal ) == aOnValues.end() )
11556 {
11557 aOnValues[ rVal ] = 1;
11558 }
11559 else
11560 {
11561 bIsUnique = false;
11562 break;
11563 }
11564 }
11565 if( ! bIsUnique )
11566 {
11567 SAL_INFO("vcl.pdfwriter", "enforcing unique OnValues" );
11568 // make unique by using ascending OnValues
11569 int nKid = 0;
11570 for (auto const& nKidIndex : rGroupWidget.m_aKidsIndex)
11571 {
11572 PDFWidget& rKid = m_aWidgets[nKidIndex];
11573 rKid.m_aOnValue = OUString::number( nKid+1 );
11574 if( rKid.m_aValue != "Off" )
11575 rKid.m_aValue = rKid.m_aOnValue;
11576 ++nKid;
11577 }
11578 }
11579 // finally move the "Yes" appearance to the OnValue appearance
11580 for (auto const& nKidIndex : rGroupWidget.m_aKidsIndex)
11581 {
11582 PDFWidget& rKid = m_aWidgets[nKidIndex];
11583 if ( !rKid.m_aOnValue.isEmpty() )
11584 {
11585 auto app_it = rKid.m_aAppearances.find( "N" );
11586 if( app_it != rKid.m_aAppearances.end() )
11587 {
11588 auto stream_it = app_it->second.find( "Yes" );
11589 if( stream_it != app_it->second.end() )
11590 {
11591 SvMemoryStream* pStream = stream_it->second;
11592 app_it->second.erase( stream_it );
11593 OStringBuffer aBuf( rKid.m_aOnValue.getLength()*2 );
11594 appendName( rKid.m_aOnValue, aBuf );
11595 (app_it->second)[ aBuf.makeStringAndClear() ] = pStream;
11596 }
11597 else
11598 SAL_INFO("vcl.pdfwriter", "error: RadioButton without \"Yes\" stream" );
11599 }
11600 }
11601
11602 if ( !rKid.m_aOffValue.isEmpty() )
11603 {
11604 auto app_it = rKid.m_aAppearances.find( "N" );
11605 if( app_it != rKid.m_aAppearances.end() )
11606 {
11607 auto stream_it = app_it->second.find( "Off" );
11608 if( stream_it != app_it->second.end() )
11609 {
11610 SvMemoryStream* pStream = stream_it->second;
11611 app_it->second.erase( stream_it );
11612 OStringBuffer aBuf( rKid.m_aOffValue.getLength()*2 );
11613 appendName( rKid.m_aOffValue, aBuf );
11614 (app_it->second)[ aBuf.makeStringAndClear() ] = pStream;
11615 }
11616 else
11617 SAL_INFO("vcl.pdfwriter", "error: RadioButton without \"Off\" stream" );
11618 }
11619 }
11620
11621 // update selected radio button
11622 if( rKid.m_aValue != "Off" )
11623 {
11624 rGroupWidget.m_aValue = rKid.m_aValue;
11625 }
11626 }
11627 }
11628}
11629
11631{
11632 sal_Int32 nRadioGroupWidget = -1;
11633
11634 std::map< sal_Int32, sal_Int32 >::const_iterator it = m_aRadioGroupWidgets.find( rBtn.RadioGroup );
11635
11636 if( it == m_aRadioGroupWidgets.end() )
11637 {
11638 m_aRadioGroupWidgets[ rBtn.RadioGroup ] = nRadioGroupWidget =
11639 sal_Int32(m_aWidgets.size());
11640
11641 // new group, insert the radiobutton
11642 m_aWidgets.emplace_back( );
11643 m_aWidgets.back().m_nObject = createObject();
11644 m_aWidgets.back().m_nPage = m_nCurrentPage;
11645 m_aWidgets.back().m_eType = PDFWriter::RadioButton;
11646 m_aWidgets.back().m_nRadioGroup = rBtn.RadioGroup;
11647 m_aWidgets.back().m_nFlags |= 0x0000C000; // NoToggleToOff and Radio bits
11648
11649 createWidgetFieldName( sal_Int32(m_aWidgets.size()-1), rBtn );
11650 }
11651 else
11652 nRadioGroupWidget = it->second;
11653
11654 return nRadioGroupWidget;
11655}
11656
11657sal_Int32 PDFWriterImpl::createControl( const PDFWriter::AnyWidget& rControl, sal_Int32 nPageNr )
11658{
11659 if( nPageNr < 0 )
11660 nPageNr = m_nCurrentPage;
11661
11662 if( nPageNr < 0 || o3tl::make_unsigned(nPageNr) >= m_aPages.size() )
11663 return -1;
11664
11665 bool sigHidden(true);
11666 sal_Int32 nNewWidget = m_aWidgets.size();
11667 m_aWidgets.emplace_back( );
11668
11669 m_aWidgets.back().m_nObject = createObject();
11670 m_aWidgets.back().m_aRect = rControl.Location;
11671 m_aWidgets.back().m_nPage = nPageNr;
11672 m_aWidgets.back().m_eType = rControl.getType();
11673
11674 sal_Int32 nRadioGroupWidget = -1;
11675 // for unknown reasons the radio buttons of a radio group must not have a
11676 // field name, else the buttons are in fact check boxes -
11677 // that is multiple buttons of the radio group can be selected
11678 if( rControl.getType() == PDFWriter::RadioButton )
11679 nRadioGroupWidget = findRadioGroupWidget( static_cast<const PDFWriter::RadioButtonWidget&>(rControl) );
11680 else
11681 {
11682 createWidgetFieldName( nNewWidget, rControl );
11683 }
11684
11685 // caution: m_aWidgets must not be changed after here or rNewWidget may be invalid
11686 PDFWidget& rNewWidget = m_aWidgets[nNewWidget];
11687 rNewWidget.m_aDescription = rControl.Description;
11688 rNewWidget.m_aText = rControl.Text;
11689 rNewWidget.m_nTextStyle = rControl.TextStyle &
11693 rNewWidget.m_nTabOrder = rControl.TabOrder;
11694
11695 // various properties are set via the flags (/Ff) property of the field dict
11696 if( rControl.ReadOnly )
11697 rNewWidget.m_nFlags |= 1;
11698 if( rControl.getType() == PDFWriter::PushButton )
11699 {
11700 const PDFWriter::PushButtonWidget& rBtn = static_cast<const PDFWriter::PushButtonWidget&>(rControl);
11701 if( rNewWidget.m_nTextStyle == DrawTextFlags::NONE )
11702 rNewWidget.m_nTextStyle =
11705
11706 rNewWidget.m_nFlags |= 0x00010000;
11707 if( !rBtn.URL.isEmpty() )
11708 rNewWidget.m_aListEntries.push_back( rBtn.URL );
11709 rNewWidget.m_bSubmit = rBtn.Submit;
11710 rNewWidget.m_bSubmitGet = rBtn.SubmitGet;
11711 rNewWidget.m_nDest = rBtn.Dest;
11712 createDefaultPushButtonAppearance( rNewWidget, rBtn );
11713 }
11714 else if( rControl.getType() == PDFWriter::RadioButton )
11715 {
11716 const PDFWriter::RadioButtonWidget& rBtn = static_cast<const PDFWriter::RadioButtonWidget&>(rControl);
11717 if( rNewWidget.m_nTextStyle == DrawTextFlags::NONE )
11718 rNewWidget.m_nTextStyle =
11720 /* PDF sees a RadioButton group as one radio button with
11721 * children which are in turn check boxes
11722 *
11723 * so we need to create a radio button on demand for a new group
11724 * and insert a checkbox for each RadioButtonWidget as its child
11725 */
11726 rNewWidget.m_eType = PDFWriter::CheckBox;
11727 rNewWidget.m_nRadioGroup = rBtn.RadioGroup;
11728
11729 SAL_WARN_IF( nRadioGroupWidget < 0 || o3tl::make_unsigned(nRadioGroupWidget) >= m_aWidgets.size(), "vcl.pdfwriter", "no radio group parent" );
11730
11731 PDFWidget& rRadioButton = m_aWidgets[nRadioGroupWidget];
11732 rRadioButton.m_aKids.push_back( rNewWidget.m_nObject );
11733 rRadioButton.m_aKidsIndex.push_back( nNewWidget );
11734 rNewWidget.m_nParent = rRadioButton.m_nObject;
11735
11736 rNewWidget.m_aValue = "Off";
11737 rNewWidget.m_aOnValue = rBtn.OnValue;
11738 rNewWidget.m_aOffValue = rBtn.OffValue;
11739 if( rRadioButton.m_aValue.isEmpty() && rBtn.Selected )
11740 {
11741 rNewWidget.m_aValue = rNewWidget.m_aOnValue;
11742 rRadioButton.m_aValue = rNewWidget.m_aOnValue;
11743 }
11744 createDefaultRadioButtonAppearance( rNewWidget, rBtn );
11745
11746 // union rect of radio group
11747 tools::Rectangle aRect = rNewWidget.m_aRect;
11748 m_aPages[ nPageNr ].convertRect( aRect );
11749 rRadioButton.m_aRect.Union( aRect );
11750 }
11751 else if( rControl.getType() == PDFWriter::CheckBox )
11752 {
11753 const PDFWriter::CheckBoxWidget& rBox = static_cast<const PDFWriter::CheckBoxWidget&>(rControl);
11754 if( rNewWidget.m_nTextStyle == DrawTextFlags::NONE )
11755 rNewWidget.m_nTextStyle =
11757
11758 rNewWidget.m_aValue
11759 = rBox.Checked ? std::u16string_view(u"Yes") : std::u16string_view(u"Off" );
11760 rNewWidget.m_aOnValue = rBox.OnValue;
11761 rNewWidget.m_aOffValue = rBox.OffValue;
11762 // create default appearance before m_aRect gets transformed
11763 createDefaultCheckBoxAppearance( rNewWidget, rBox );
11764 }
11765 else if( rControl.getType() == PDFWriter::ListBox )
11766 {
11767 if( rNewWidget.m_nTextStyle == DrawTextFlags::NONE )
11769
11770 const PDFWriter::ListBoxWidget& rLstBox = static_cast<const PDFWriter::ListBoxWidget&>(rControl);
11771 rNewWidget.m_aListEntries = rLstBox.Entries;
11772 rNewWidget.m_aSelectedEntries = rLstBox.SelectedEntries;
11773 rNewWidget.m_aValue = rLstBox.Text;
11774 if( rLstBox.DropDown )
11775 rNewWidget.m_nFlags |= 0x00020000;
11776 if (rLstBox.MultiSelect && !rLstBox.DropDown)
11777 rNewWidget.m_nFlags |= 0x00200000;
11778
11779 createDefaultListBoxAppearance( rNewWidget, rLstBox );
11780 }
11781 else if( rControl.getType() == PDFWriter::ComboBox )
11782 {
11783 if( rNewWidget.m_nTextStyle == DrawTextFlags::NONE )
11785
11786 const PDFWriter::ComboBoxWidget& rBox = static_cast<const PDFWriter::ComboBoxWidget&>(rControl);
11787 rNewWidget.m_aValue = rBox.Text;
11788 rNewWidget.m_aListEntries = rBox.Entries;
11789 rNewWidget.m_nFlags |= 0x00060000; // combo and edit flag
11790
11792 aLBox.Name = rBox.Name;
11793 aLBox.Description = rBox.Description;
11794 aLBox.Text = rBox.Text;
11795 aLBox.TextStyle = rBox.TextStyle;
11796 aLBox.ReadOnly = rBox.ReadOnly;
11797 aLBox.Border = rBox.Border;
11798 aLBox.BorderColor = rBox.BorderColor;
11799 aLBox.Background = rBox.Background;
11800 aLBox.BackgroundColor = rBox.BackgroundColor;
11801 aLBox.TextFont = rBox.TextFont;
11802 aLBox.TextColor = rBox.TextColor;
11803 aLBox.DropDown = true;
11804 aLBox.MultiSelect = false;
11805 aLBox.Entries = rBox.Entries;
11806
11807 createDefaultListBoxAppearance( rNewWidget, aLBox );
11808 }
11809 else if( rControl.getType() == PDFWriter::Edit )
11810 {
11811 if( rNewWidget.m_nTextStyle == DrawTextFlags::NONE )
11813
11814 const PDFWriter::EditWidget& rEdit = static_cast<const PDFWriter::EditWidget&>(rControl);
11815 if( rEdit.MultiLine )
11816 {
11817 rNewWidget.m_nFlags |= 0x00001000;
11819 }
11820 if( rEdit.Password )
11821 rNewWidget.m_nFlags |= 0x00002000;
11822 if (rEdit.FileSelect)
11823 rNewWidget.m_nFlags |= 0x00100000;
11824 rNewWidget.m_nMaxLen = rEdit.MaxLen;
11825 rNewWidget.m_nFormat = rEdit.Format;
11826 rNewWidget.m_aCurrencySymbol = rEdit.CurrencySymbol;
11827 rNewWidget.m_nDecimalAccuracy = rEdit.DecimalAccuracy;
11829 rNewWidget.m_aTimeFormat = rEdit.TimeFormat;
11830 rNewWidget.m_aDateFormat = rEdit.DateFormat;
11831 rNewWidget.m_aValue = rEdit.Text;
11832
11833 createDefaultEditAppearance( rNewWidget, rEdit );
11834 }
11835#if HAVE_FEATURE_NSS
11836 else if( rControl.getType() == PDFWriter::Signature)
11837 {
11838 sigHidden = true;
11839
11840 rNewWidget.m_aRect = tools::Rectangle(0, 0, 0, 0);
11841
11843 rNewWidget.m_aValue = OUString::number( m_nSignatureObject );
11844 rNewWidget.m_aValue += " 0 R";
11845 // let's add a fake appearance
11846 rNewWidget.m_aAppearances[ "N" ][ "Standard" ] = new SvMemoryStream();
11847 }
11848#endif
11849
11850 // if control is a hidden signature, do not convert coordinates since we
11851 // need /Rect [ 0 0 0 0 ]
11852 if ( ! ( ( rControl.getType() == PDFWriter::Signature ) && sigHidden ) )
11853 {
11854 // convert to default user space now, since the mapmode may change
11855 // note: create default appearances before m_aRect gets transformed
11856 m_aPages[ nPageNr ].convertRect( rNewWidget.m_aRect );
11857 }
11858
11859 // insert widget to page's annotation list
11860 m_aPages[ nPageNr ].m_aAnnotations.push_back( rNewWidget.m_nObject );
11861
11862 return nNewWidget;
11863}
11864
11865void PDFWriterImpl::MARK( const char* pString )
11866{
11869 emitComment( pString );
11870}
11871
11873{
11874 if (m_nFormObject > 0)
11875 return m_nFormObject;
11876 else
11877 return m_nBitmapObject;
11878}
11879}
11880
11881/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
DrawTextFlags
sal_Int32 nLineWidth
constexpr float ARTIFICIAL_ITALIC_SKEW
PropFlags nPropertyId
constexpr auto convertMm100ToPoint(N n)
const char * pName
Reference< XInputStream > xStream
size_t nCurPoint
BitmapChecksum vcl_get_checksum(BitmapChecksum Checksum, const void *Data, sal_uInt32 DatLen)
Definition: checksum.hxx:72
Bitmap const & GetBitmap() const
Definition: alpha.cxx:77
bool IsEmpty() const
Base class used mainly for the LibreOffice Desktop class.
Definition: svapp.hxx:237
static OutputDevice * GetDefaultDevice()
Get the default "device" (in this case the default window).
Definition: svapp.cxx:1043
Container for the binary data, whose responsibility is to manage the make it as simple as possible to...
const AlphaMask & GetAlphaMask() const
Definition: bitmapex.hxx:71
bool Convert(BmpConversion eConversion)
Convert bitmap format.
Definition: BitmapEx.cxx:383
bool IsAlpha() const
Definition: BitmapEx.cxx:207
Bitmap GetBitmap(Color aTransparentReplaceColor) const
Definition: BitmapEx.cxx:217
const MapMode & GetPrefMapMode() const
Definition: bitmapex.hxx:79
const Size & GetPrefSize() const
Definition: bitmapex.hxx:76
const Size & GetSizePixel() const
Definition: bitmapex.hxx:73
tools::Long Height() const
tools::Long Width() const
const BitmapPalette & GetPalette() const
sal_uInt16 GetBestPaletteIndex(const BitmapColor &rBitmapColor) const
ScanlineFormat GetScanlineFormat() const
sal_uInt16 GetPaletteEntryCount() const
sal_uInt16 GetBitCount() const
const BitmapColor & GetPaletteColor(sal_uInt16 nColor) const
BitmapColor GetColor(tools::Long nY, tools::Long nX) const
Scanline GetScanline(tools::Long nY) const
bool Convert(BmpConversion eConversion)
Convert bitmap format.
BitmapChecksum GetChecksum() const
Size GetSizePixel() const
bool HasGreyPaletteAny() const
vcl::PixelFormat getPixelFormat() const
sal_uInt8 GetLuminance() const
sal_uInt8 GetBlue() const
sal_uInt8 GetRed() const
bool IsTransparent() const
sal_uInt8 GetGreen() const
bool IsMicrosoftSymbolEncoded() const
FontFamily GetFamilyType() const
FontItalic GetItalic() const
const OUString & GetFamilyName() const
FontPitch GetPitch() const
tools::Long GetLineHeight() const
Definition: metric.hxx:47
tools::Long GetDescent() const
Definition: metric.hxx:44
tools::Long GetAscent() const
Definition: metric.hxx:43
tools::Rectangle m_aFontBBox
Definition: fontsubset.hxx:71
OUString m_aPSName
Definition: fontsubset.hxx:67
FontType m_nFontType
font-type of subset result
Definition: fontsubset.hxx:72
int m_nAscent
all metrics in PS font units
Definition: fontsubset.hxx:68
bool GetGlyphBoundRect(const LogicalFontInstance *, tools::Rectangle &) const
bool IsSpacing() const
sal_GlyphId glyphId() const
bool IsClusterStart() const
int charPos() const
double origWidth() const
int charCount() const
double newWidth() const
bool IsVertical() const
bool IsInCluster() const
Degree10 GetAngle() const
sal_uInt16 GetBorder() const
const Color & GetEndColor() const
const Color & GetStartColor() const
css::awt::GradientStyle GetStyle() const
void GetBoundRect(const tools::Rectangle &rRect, tools::Rectangle &rBoundRect, Point &rCenter) const
static ErrCode Import(SvStream &rIStm, Graphic &rGraphic, ConvertDataFormat nFormat=ConvertDataFormat::Unknown)
Definition: cvtgrf.cxx:30
Size GetPrefSize() const
Definition: graph.cxx:364
BitmapEx GetBitmapEx(const GraphicConversionParameters &rParameters=GraphicConversionParameters()) const
Definition: graph.cxx:330
Size GetSizePixel(const OutputDevice *pRefDevice=nullptr) const
Definition: graph.cxx:412
const std::shared_ptr< VectorGraphicData > & getVectorGraphicData() const
Definition: graph.cxx:525
Definition: hatch.hxx:47
const Color & GetColor() const
Definition: hatch.hxx:62
OUString GetMark(DecodeMechanism eMechanism=DecodeMechanism::ToIUri, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
static OUString GetRelURL(std::u16string_view rTheBaseURIRef, OUString const &rTheAbsURIRef, EncodeMechanism eEncodeMechanism=EncodeMechanism::WasEncoded, DecodeMechanism eDecodeMechanism=DecodeMechanism::ToIUri, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8, FSysStyle eStyle=FSysStyle::Detect)
INetProtocol GetProtocol() const
bool SetMark(std::u16string_view rTheFragment, EncodeMechanism eMechanism=EncodeMechanism::WasEncoded, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8)
sal_Int32 Count() const
const ImplTextLineInfo & GetLine(sal_Int32 nLine) const
sal_Int32 GetIndex() const
sal_Int32 GetLen() const
tools::Long GetWidth() const
const css::lang::Locale & getLocale(bool bResolveSystem=true) const
void getIsoLanguageScriptCountry(OUString &rLanguage, OUString &rScript, OUString &rCountry) const
basegfx::B2DPolyPolygon GetGlyphOutlineUntransformed(sal_GlyphId) const
const vcl::font::PhysicalFontFace * GetFontFace() const
double GetGlyphWidth(sal_GlyphId, bool=false, bool=true) const
FontMetricDataRef mxFontMetric
const vcl::font::FontSelectPattern & GetFontSelectPattern() const
sal_GlyphId GetGlyphIndex(uint32_t, uint32_t=0) const
void SetOrigin(const Point &rOrigin)
Definition: mapmod.cxx:138
const Fraction & GetScaleX() const
Definition: mapmod.cxx:185
MapUnit GetMapUnit() const
Definition: mapmod.cxx:181
const Point & GetOrigin() const
Definition: mapmod.cxx:183
const Fraction & GetScaleY() const
Definition: mapmod.cxx:187
Some things multiple-inherit from VclAbstractDialog and OutputDevice, so we need to use virtual inher...
Definition: outdev.hxx:170
SAL_DLLPRIVATE sal_Int32 GetDPIX() const
Get the output device's DPI x-axis value.
Definition: outdev.hxx:385
LogicalFontInstance const * GetFontInstance() const
basegfx::B2DHomMatrix GetViewTransformation() const
Definition: map.cxx:793
SAL_DLLPRIVATE tools::Long ImplDevicePixelToLogicHeight(tools::Long nHeight) const
Convert device pixels to a height in logical units.
Definition: map.cxx:304
void SetFont(const vcl::Font &rNewFont)
Definition: outdev/font.cxx:56
std::shared_ptr< ImplFontCache > mxFontCache
Definition: outdev.hxx:262
SAL_DLLPRIVATE sal_Int32 GetDPIY() const
Get the output device's DPI y-axis value.
Definition: outdev.hxx:391
virtual bool ImplNewFont() const
basegfx::B2DHomMatrix GetInverseViewTransformation() const
Definition: map.cxx:821
SAL_WARN_UNUSED_RESULT Point PixelToLogic(const Point &rDevicePt) const
Definition: map.cxx:1110
std::shared_ptr< vcl::font::PhysicalFontCollection > mxFontCollection
Definition: outdev.hxx:261
void DrawHatch(const tools::PolyPolygon &rPolyPoly, const Hatch &rHatch)
SAL_WARN_UNUSED_RESULT Point LogicToLogic(const Point &rPtSource, const MapMode *pMapModeSource, const MapMode *pMapModeDest) const
Definition: map.cxx:1580
void SetMapMode()
Definition: map.cxx:597
OUString GetEllipsisString(const OUString &rStr, tools::Long nMaxWidth, DrawTextFlags nStyle=DrawTextFlags::EndEllipsis) const
Definition: text.cxx:2017
SAL_DLLPRIVATE Point SubPixelToLogic(const basegfx::B2DPoint &rDevicePt) const
Definition: map.cxx:1122
SAL_DLLPRIVATE tools::Long GetEmphasisDescent() const
Definition: outdev.hxx:1182
SAL_DLLPRIVATE void SetDPIY(sal_Int32 nDPIY)
Definition: outdev.hxx:394
tools::Long GetTextWidth(const OUString &rStr, sal_Int32 nIndex=0, sal_Int32 nLen=-1, vcl::text::TextLayoutCache const *=nullptr, SalLayoutGlyphs const *const pLayoutCache=nullptr) const
Width of the text.
Definition: text.cxx:886
void SetDigitLanguage(LanguageType)
Definition: text.cxx:71
vcl::text::ComplexTextLayoutFlags GetLayoutMode() const
Definition: outdev.hxx:490
SAL_DLLPRIVATE tools::Long ImplDevicePixelToLogicWidth(tools::Long nWidth) const
Convert device pixels to a width in logical units.
Definition: map.cxx:296
FontMetric GetFontMetric() const
SAL_DLLPRIVATE void ImplInitTextLineSize()
Definition: textline.cxx:101
static SAL_DLLPRIVATE tools::Long ImplGetTextLines(const tools::Rectangle &rRect, tools::Long nTextHeight, ImplMultiTextLineInfo &rLineInfo, tools::Long nWidth, const OUString &rStr, DrawTextFlags nStyle, const vcl::ITextLayout &_rLayout)
Definition: text.cxx:485
const MapMode & GetMapMode() const
Definition: outdev.hxx:1557
SAL_DLLPRIVATE void ImplInitAboveTextLineSize()
Definition: textline.cxx:106
SAL_DLLPRIVATE tools::Long GetEmphasisAscent() const
Definition: outdev.hxx:1181
void Push(vcl::PushFlags nFlags=vcl::PushFlags::ALL)
Definition: stack.cxx:32
tools::Long GetTextHeight() const
Height where any character of the current font fits; in logic coordinates.
Definition: text.cxx:897
void Pop()
Definition: stack.cxx:91
std::unique_ptr< SalLayout > ImplLayout(const OUString &, sal_Int32 nIndex, sal_Int32 nLen, const Point &rLogicPos=Point(0, 0), tools::Long nLogicWidth=0, KernArraySpan aKernArray=KernArraySpan(), o3tl::span< const sal_Bool > pKashidaArray={}, SalLayoutFlags flags=SalLayoutFlags::NONE, vcl::text::TextLayoutCache const *=nullptr, const SalLayoutGlyphs *pGlyphs=nullptr) const
Definition: text.cxx:1292
void SetLayoutMode(vcl::text::ComplexTextLayoutFlags nTextLayoutMode)
Definition: text.cxx:60
SAL_DLLPRIVATE void SetDPIX(sal_Int32 nDPIX)
Definition: outdev.hxx:393
SAL_DLLPRIVATE void ImplUpdateFontData()
constexpr tools::Long Y() const
void setX(tools::Long nX)
void setY(tools::Long nY)
tools::Long AdjustY(tools::Long nVertMove)
tools::Long AdjustX(tools::Long nHorzMove)
constexpr tools::Long X() const
static SalLayoutGlyphsCache * self()
virtual double GetTextWidth() const
Definition: vcllayout.hxx:98
basegfx::B2DPoint GetDrawPosition(const basegfx::B2DPoint &rRelative=basegfx::B2DPoint(0, 0)) const
Definition: sallayout.cxx:144
basegfx::B2DPoint & DrawBase()
Definition: vcllayout.hxx:72
Point & DrawOffset()
Definition: vcllayout.hxx:74
virtual bool GetNextGlyph(const GlyphItem **pGlyph, basegfx::B2DPoint &rPos, int &nStart, const LogicalFontInstance **ppGlyphFont=nullptr) const =0
constexpr tools::Long Height() const
tools::Long AdjustHeight(tools::Long n)
tools::Long AdjustWidth(tools::Long n)
void setHeight(tools::Long nHeight)
constexpr tools::Long Width() const
void SetStandardStyles()
const Color & GetDarkShadowColor() const
const Color & GetFieldTextColor() const
const Color & GetShadowColor() const
const Color & GetFieldColor() const
const Color & GetRadioCheckTextColor() const
const vcl::Font & GetRadioCheckFont() const
const vcl::Font & GetFieldFont() const
const Color & GetCheckedColor() const
const vcl::Font & GetPushButtonFont() const
const Color & GetLightBorderColor() const
const Color & GetLightColor() const
const Color & GetDialogColor() const
const Color & GetButtonTextColor() const
const void * GetData()
sal_uInt64 GetSize()
virtual sal_uInt64 TellEnd() override
sal_uInt64 Tell() const
std::size_t WriteBytes(const void *pData, std::size_t nSize)
bool SetStreamSize(sal_uInt64 nSize)
sal_uInt64 Seek(sal_uInt64 nPos)
SvStream & WriteStream(SvStream &rStream)
A thin wrapper around rtl::Reference to implement the acquire and dispose semantics we want for refer...
Definition: vclptr.hxx:58
bool SetOutputSizePixel(const Size &rNewSize, bool bErase=true, bool bAlphaMaskTransparent=false)
Definition: virdev.cxx:405
void SetReferenceDevice(RefDevMode)
Definition: virdev.cxx:426
virtual void dispose() override
Definition: virdev.cxx:230
bool IsRect() const
Definition: wall.cxx:232
const BitmapEx & GetBitmap() const
Definition: wall.cxx:184
const Color & GetColor() const
Definition: wall.hxx:71
bool IsBitmap() const
Definition: wall.cxx:189
WallpaperStyle GetStyle() const
Definition: wall.hxx:74
bool IsGradient() const
Definition: wall.cxx:213
Gradient GetGradient() const
Definition: wall.cxx:203
const tools::Rectangle & GetRect() const
Definition: wall.hxx:85
tools::Long EndCompression()
void BeginCompression(int nCompressLevel=ZCODEC_DEFAULT_COMPRESSION, bool gzLib=false)
void Write(SvStream &rOStm, const sal_uInt8 *pData, sal_uInt32 nSize)
double c() const
double e() const
double a() const
void rotate(double fRadiant)
void translate(double fX, double fY)
double f() const
double d() const
double b() const
B2DPolygon const & getB2DPolygon(sal_uInt32 nIndex) const
void transform(const basegfx::B2DHomMatrix &rMatrix)
sal_uInt32 count() const
bool isPrevControlPointUsed(sal_uInt32 nIndex) const
bool isNextControlPointUsed(sal_uInt32 nIndex) const
bool isClosed() const
basegfx::B2DPoint const & getB2DPoint(sal_uInt32 nIndex) const
basegfx::B2DPoint getPrevControlPoint(sal_uInt32 nIndex) const
B2DRange const & getB2DRange() const
sal_uInt32 count() const
basegfx::B2DPoint getNextControlPoint(sal_uInt32 nIndex) const
BASEGFX_DLLPUBLIC void transform(const B2DHomMatrix &rMatrix)
TYPE getWidth() const
TYPE getMinX() const
TYPE getMaxY() const
TYPE getHeight() const
TYPE getX() const
void adjustX(TYPE fX)
TYPE getY() const
std::vector< unsigned char > finalize()
void update(const unsigned char *pInput, size_t length)
sal_uInt16 Count() const
void Move(tools::Long nHorzMove, tools::Long nVertMove)
const tools::Polygon & GetObject(sal_uInt16 nPos) const
tools::Rectangle GetBoundRect() const
void Optimize(PolyOptimizeFlags nOptimizeFlags)
::basegfx::B2DPolygon getB2DPolygon() const
sal_uInt16 GetSize() const
void Move(tools::Long nHorzMove, tools::Long nVertMove)
const PolyFlags * GetConstFlagAry() const
tools::Rectangle GetBoundRect() const
void Rotate(const Point &rCenter, double fSin, double fCos)
constexpr tools::Long GetWidth() const
constexpr void SetLeft(tools::Long v)
constexpr void SetTop(tools::Long v)
tools::Long getY() const
constexpr tools::Long Top() const
constexpr Point TopLeft() const
constexpr void SetRight(tools::Long v)
constexpr Size GetSize() const
constexpr tools::Long Right() const
tools::Long AdjustTop(tools::Long nVertMoveDelta)
constexpr Point BottomCenter() const
tools::Long AdjustRight(tools::Long nHorzMoveDelta)
constexpr void SetBottom(tools::Long v)
constexpr Point BottomRight() const
constexpr tools::Long GetHeight() const
tools::Rectangle & Union(const tools::Rectangle &rRect)
tools::Long AdjustBottom(tools::Long nVertMoveDelta)
tools::Long getX() const
tools::Long AdjustLeft(tools::Long nHorzMoveDelta)
constexpr Point TopCenter() const
void setHeight(tools::Long n)
constexpr tools::Long Left() const
constexpr tools::Long Bottom() const
constexpr bool IsEmpty() const
constexpr Point BottomLeft() const
is an implementation of the ITextLayout interface which simply delegates its calls to the respective ...
Definition: textlayout.hxx:47
ExternalPDFStream & get(sal_uInt32 nIndex)
sal_Int32 store(BinaryDataContainer const &rDataContainer)
tools::Long GetFontHeight() const
Definition: font/font.cxx:909
void SetFontSize(const Size &)
Definition: font/font.cxx:149
FontFamily GetFamilyType()
Definition: font/font.cxx:928
void SetOutline(bool bOutline)
Definition: font/font.cxx:254
void SetAverageFontWidth(tools::Long nWidth)
Definition: font/font.cxx:910
void SetColor(const Color &)
Definition: font/font.cxx:107
FontStrikeout GetStrikeout() const
Definition: font/font.cxx:946
bool IsShadow() const
Definition: font/font.cxx:942
FontLineStyle GetOverline() const
Definition: font/font.cxx:945
FontRelief GetRelief() const
Definition: font/font.cxx:943
FontEmphasisMark GetEmphasisMark() const
Definition: font/font.cxx:947
FontItalic GetItalic()
Definition: font/font.cxx:927
void SetItalic(FontItalic)
Definition: font/font.cxx:248
void SetWeight(FontWeight)
Definition: font/font.cxx:236
const OUString & GetFamilyName() const
Definition: font/font.cxx:904
TextAlign GetAlignment() const
Definition: font/font.cxx:902
const Size & GetFontSize() const
Definition: font/font.cxx:907
bool IsUnderlineAbove() const
const Color & GetColor() const
Definition: font/font.cxx:898
FontWeight GetWeight()
Definition: font/font.cxx:925
void SetFamilyName(const OUString &rFamilyName)
Definition: font/font.cxx:137
FontLineStyle GetUnderline() const
Definition: font/font.cxx:944
bool IsVertical() const
Definition: font/font.cxx:921
void SetShadow(bool bShadow)
Definition: font/font.cxx:260
void SetRelief(FontRelief)
Definition: font/font.cxx:284
bool IsOutline() const
Definition: font/font.cxx:941
bool IsWordLineMode() const
Definition: font/font.cxx:948
Degree10 GetOrientation() const
Definition: font/font.cxx:920
FontEmphasisMark GetEmphasisMarkStyle() const
Definition: font/font.cxx:314
const Color & GetFillColor() const
Definition: font/font.cxx:899
tools::Long GetAverageFontWidth() const
Definition: font/font.cxx:911
bool writeBuffer(std::string_view aBuffer)
Copies objects from one PDF file into another one.
static sal_Int32 copyPageStreams(std::vector< filter::PDFObjectElement * > &rContentStreams, SvMemoryStream &rStream, bool &rCompressed)
Copies page one or more page streams from rContentStreams into rStream.
void copyPageResources(filter::PDFObjectElement *pPage, OStringBuffer &rLine)
Copies resources of pPage into rLine.
sal_Int32 copyExternalResource(SvMemoryStream &rDocBuffer, filter::PDFObjectElement &rObject, std::map< sal_Int32, sal_Int32 > &rCopiedResources)
Copies a single resource from an external document, returns the new object ID in our document.
PDFStreamIf(PDFWriterImpl *pWriter)
virtual void SAL_CALL flush() override
virtual void SAL_CALL closeOutput() override
virtual void SAL_CALL writeBytes(const css::uno::Sequence< sal_Int8 > &aData) override
VclPtr< PDFWriterImpl > m_pWriter
static void computeDocumentIdentifier(std::vector< sal_uInt8 > &o_rIdentifier, const vcl::PDFWriter::PDFDocInfo &i_rDocInfo, const OString &i_rCString1, OString &o_rCString2)
std::list< GradientEmit > m_aGradients
sal_Int64 m_nSignatureContentOffset
void drawWallpaper(const tools::Rectangle &rRect, const Wallpaper &rWall)
void setTextAlign(TextAlign eAlign)
std::map< sal_Int32, sal_Int32 > m_aLinkPropertyMap
void moveClipRegion(sal_Int32 nX, sal_Int32 nY)
sal_Int64 m_nSignatureLastByteRangeNoOffset
bool updateObject(sal_Int32 n) override
See vcl::PDFObjectContainer::updateObject().
void beginStructureElement(sal_Int32 id)
void drawEllipse(const tools::Rectangle &rRect)
void addInternalStructureContainer(PDFStructureElement &rEle)
void enableStringEncryption(sal_Int32 nObject)
void setFont(const Font &rFont)
sal_Int32 findRadioGroupWidget(const PDFWriter::RadioButtonWidget &rRadio)
void setAlternateText(const OUString &rText)
sal_Int32 emitBuildinFont(const pdf::BuildinFontFace *, sal_Int32 nObject)
sal_Int32 emitStructParentTree(sal_Int32 nTreeObject)
sal_Int32 emitFontDescriptor(const vcl::font::PhysicalFontFace *, FontSubsetInfo const &, sal_Int32 nSubsetID, sal_Int32 nStream)
static void convertLineInfoToExtLineInfo(const LineInfo &rIn, PDFWriter::ExtLineInfo &rOut)
std::set< PDFWriter::ErrorCode > m_aErrors
sal_Int32 addEmbeddedFile(BinaryDataContainer const &rDataContainer)
void createNote(const tools::Rectangle &rRect, const PDFNote &rNote, sal_Int32 nPageNr)
std::map< const vcl::font::PhysicalFontFace *, FontSubset > m_aSubsets
void setActualText(const OUString &rText)
std::map< const vcl::font::PhysicalFontFace *, EmbedFont > m_aSystemFonts
static void emitPopupAnnotationLine(OStringBuffer &aLine, PDFPopupAnnotation const &rPopUp)
void drawPolyLine(const tools::Polygon &rPoly)
std::list< GraphicsState > m_aGraphicsStack
GraphicsState m_aCurrentPDFState
std::map< const vcl::font::PhysicalFontFace *, FontSubset > m_aType3Fonts
std::unordered_map< OString, OString > m_aRoleMap
void createWidgetFieldName(sal_Int32 i_nWidgetsIndex, const PDFWriter::AnyWidget &i_rInWidget)
sal_Int32 createControl(const PDFWriter::AnyWidget &rControl, sal_Int32 nPageNr=-1)
void writeTransparentObject(TransparencyEmit &rObject)
sal_Int32 m_nCurrentStructElement
void drawVerticalGlyphs(const std::vector< PDFGlyph > &rGlyphs, OStringBuffer &rLine, const Point &rAlignOffset, const Matrix3 &rRotScale, double fAngle, double fXScale, sal_Int32 nFontHeight)
const char * getStructureTag(PDFWriter::StructElement)
void drawPolyPolygon(const tools::PolyPolygon &rPolyPoly)
void endStructureElementMCSeq(EndMode=EndMode::Default)
void addRoleMap(OString aAlias, PDFWriter::StructElement eType)
OString emitStructureAttributes(PDFStructureElement &rEle)
std::unordered_set< sal_Int32 > m_StructElemObjsWithID
std::vector< PDFLink > m_aLinks
std::vector< PDFEmbeddedFile > m_aEmbeddedFiles
Contains embedded files.
static sal_Int32 computeAccessPermissions(const vcl::PDFWriter::PDFEncryptionProperties &i_rProperties, sal_Int32 &o_rKeyLength, sal_Int32 &o_rRC4KeyLength)
sal_Int32 createLink(const tools::Rectangle &rRect, sal_Int32 nPageNr, OUString const &rAltText)
void appendLiteralStringEncrypt(std::u16string_view rInString, const sal_Int32 nInObjectNumber, OStringBuffer &rOutBuffer, rtl_TextEncoding nEnc=RTL_TEXTENCODING_ASCII_US)
std::map< sal_Int32, sal_Int32 > m_aRadioGroupWidgets
void initStructureElement(sal_Int32 id, PDFWriter::StructElement eType, std::u16string_view rAlias)
sal_Int32 ensureStructureElement()
std::list< StreamRedirect > m_aOutputStreams
void drawGradient(const tools::Rectangle &rRect, const Gradient &rGradient)
friend class PDFStreamIf
void drawTextLine(const Point &rPos, tools::Long nWidth, FontStrikeout eStrikeout, FontLineStyle eUnderline, FontLineStyle eOverline, bool bUnderlineAbove)
OString m_aCreationMetaDateString
void newPage(double nPageWidth, double nPageHeight, PDFWriter::Orientation eOrientation)
void drawStraightTextLine(OStringBuffer &aLine, tools::Long nWidth, FontLineStyle eTextLine, Color aColor, bool bIsAbove)
void dispose() override
void setOutlineItemDest(sal_Int32 nItem, sal_Int32 nDestID)
bool writeGradientFunction(GradientEmit const &rObject)
void drawText(const Point &rPos, const OUString &rText, sal_Int32 nIndex, sal_Int32 nLen, bool bTextLines=true)
std::map< sal_Int32, sal_Int32 > emitSystemFont(const vcl::font::PhysicalFontFace *, EmbedFont const &)
void createDefaultRadioButtonAppearance(PDFWidget &, const PDFWriter::RadioButtonWidget &rWidget)
void drawStretchText(const Point &rPos, sal_Int32 nWidth, const OUString &rText, sal_Int32 nIndex, sal_Int32 nLen)
void writeG4Stream(BitmapReadAccess const *i_pBitmap)
PDFWriter::PDFWriterContext m_aContext
void createDefaultPushButtonAppearance(PDFWidget &, const PDFWriter::PushButtonWidget &rWidget)
void drawStrikeoutLine(OStringBuffer &aLine, tools::Long nWidth, FontStrikeout eStrikeout, Color aColor)
void drawHorizontalGlyphs(const std::vector< PDFGlyph > &rGlyphs, OStringBuffer &rLine, const Point &rAlignOffset, bool bFirst, double fAngle, double fXScale, sal_Int32 nFontHeight, sal_Int32 nPixelFontHeight)
void setOutlineItemText(sal_Int32 nItem, std::u16string_view rText)
std::list< BitmapEmit > m_aBitmaps
PDFWriterImpl(const PDFWriter::PDFWriterContext &rContext, const css::uno::Reference< css::beans::XMaterialHolder > &, PDFWriter &)
std::vector< PDFNamedDest > m_aNamedDests
bool checkEmitStructure()
checks whether a non struct element lies in the ancestor hierarchy of the current structure element
void setMapMode(const MapMode &rMapMode)
sal_Int32 emitDocumentMetadata()
void setOutlineItemParent(sal_Int32 nItem, sal_Int32 nNewParent)
void checkAndEnableStreamEncryption(sal_Int32 nObject) override
bool emitEmbeddedFiles()
Writes embedded files.
sal_Int32 updateOutlineItemCount(std::vector< sal_Int32 > &rCounts, sal_Int32 nItemLevel, sal_Int32 nCurrentItemId)
void setLinkURL(sal_Int32 nLinkId, const OUString &rURL)
void endTransparencyGroup(const tools::Rectangle &rBoundingBox, sal_uInt32 nTransparentPercent)
std::unique_ptr< SvMemoryStream > m_pMemStream
void disableStreamEncryption() override
::comphelper::Hash m_DocDigest
void setScreenStream(sal_Int32 nScreenId, const OUString &rURL)
void drawArc(const tools::Rectangle &rRect, const Point &rStart, const Point &rStop, bool bWithPie, bool bWidthChord)
void drawStrikeoutChar(const Point &rPos, tools::Long nWidth, FontStrikeout eStrikeout)
std::vector< PDFPage > m_aPages
void appendBuildinFontsToDict(OStringBuffer &rDict) const
std::vector< sal_uInt8 > m_vEncryptionBuffer
void createDefaultEditAppearance(PDFWidget &, const PDFWriter::EditWidget &rWidget)
StyleSettings m_aWidgetStyleSettings
const MapMode & getMapMode()
sal_Int32 getFontDictObject()
void writeReferenceXObject(const ReferenceXObjectEmit &rEmit)
Writes the form XObject proxy for the image.
void setStructureBoundingBox(const tools::Rectangle &rRect)
void createDefaultListBoxAppearance(PDFWidget &, const PDFWriter::ListBoxWidget &rWidget)
std::unordered_map< OString, sal_Int32 > m_aFieldNameMap
std::vector< TransparencyEmit > m_aTransparentObjects
void drawEmphasisMark(tools::Long nX, tools::Long nY, const tools::PolyPolygon &rPolyPoly, bool bPolyLine, const tools::Rectangle &rRect1, const tools::Rectangle &rRect2)
void createEmbeddedFile(const Graphic &rGraphic, ReferenceXObjectEmit &rEmit, sal_Int32 nBitmapObject)
Stores the original PDF data from rGraphic as an embedded file.
static const sal_uInt32 ncMaxPDFArraySize
bool writeBufferBytes(const void *pBuffer, sal_uInt64 nBytes) override
See vcl::PDFObjectContainer::writeBuffer().
sal_Int32 createScreen(const tools::Rectangle &rRect, sal_Int32 nPageNr, OUString const &rAltText, OUString const &rMimeType)
bool setStructureAttribute(enum PDFWriter::StructAttribute eAttr, enum PDFWriter::StructAttributeValue eVal)
std::vector< OString > m_aStructParentTree
void drawRelief(SalLayout &rLayout, const OUString &rText, bool bTextLines)
const BitmapEmit & createBitmapEmit(const BitmapEx &rBitmapEx, const Graphic &rGraphic, std::list< BitmapEmit > &rBitmaps, ResourceDict &rResourceDict, std::list< StreamRedirect > &rOutputStreams)
std::vector< PDFDocumentAttachedFile > m_aDocumentAttachedFiles
void appendStrokingColor(const Color &rColor, OStringBuffer &rBuffer)
void setLinkPropertyId(sal_Int32 nLinkId, sal_Int32 nPropertyId)
static const char * getAttributeTag(PDFWriter::StructAttribute eAtr)
sal_Int32 getBestBuildinFont(const Font &rFont)
void drawJPGBitmap(SvStream &rDCTData, bool bIsTrueColor, const Size &rSizePixel, const tools::Rectangle &rTargetArea, const AlphaMask &rAlphaMask, const Graphic &rGraphic)
void setStructureAnnotIds(::std::vector< sal_Int32 > const &rAnnotIds)
sal_Int32 createNamedDest(const OUString &sDestName, const tools::Rectangle &rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType)
void drawHatch(const tools::PolyPolygon &rPolyPoly, const Hatch &rHatch)
void drawTransparent(const tools::PolyPolygon &rPolyPoly, sal_uInt32 nTransparentPercent)
std::stack< sal_Int32 > m_StructElementStack
sal_Int32 emitStructure(PDFStructureElement &rEle)
void push(PushFlags nFlags)
void setScreenURL(sal_Int32 nScreenId, const OUString &rURL)
std::vector< sal_uInt64 > m_aObjects
void AppendAnnotKid(PDFStructureElement &i_rEle, T &rAnnot)
Font drawFieldBorder(PDFWidget &, const PDFWriter::AnyWidget &, const StyleSettings &)
std::vector< TilingEmit > m_aTilings
std::map< sal_Int32, sal_Int32 > m_aBuildinFontToObjectMap
bool ImplNewFont() const override
sal_Int32 m_nAccessPermissions
void beginRedirect(SvStream *pStream, const tools::Rectangle &)
sal_Int32 emitOutputIntent()
sal_Int32 createGradient(const Gradient &rGradient, const Size &rSize)
sal_Int32 createObject() override
See vcl::PDFObjectContainer::createObject().
bool setCurrentStructureElement(sal_Int32 nElement)
void appendNonStrokingColor(const Color &rColor, OStringBuffer &rBuffer)
void createDefaultCheckBoxAppearance(PDFWidget &, const PDFWriter::CheckBoxWidget &rWidget)
void drawLine(const Point &rStart, const Point &rStop)
std::vector< PDFDest > m_aDests
void setFillColor(const Color &rColor)
void drawTextArray(const Point &rPos, const OUString &rText, KernArraySpan pDXArray, o3tl::span< const sal_Bool > pKashidaArray, sal_Int32 nIndex, sal_Int32 nLen)
void drawRectangle(const tools::Rectangle &rRect)
void drawLayout(SalLayout &rLayout, const OUString &rText, bool bTextLines)
sal_Int32 emitStructIDTree(sal_Int32 nTreeObject)
void setTextColor(const Color &rColor)
std::vector< PDFStructureElement > m_aStructure
void appendUnicodeTextStringEncrypt(const OUString &rInString, const sal_Int32 nInObjectNumber, OStringBuffer &rOutBuffer)
std::vector< JPGEmit > m_aJPGs
sal_Int32 m_nSignatureObject
bool setStructureAttributeNumerical(enum PDFWriter::StructAttribute eAttr, sal_Int32 nValue)
void drawBitmap(const Point &rDestPt, const Size &rDestSize, const BitmapEmit &rBitmap, const Color &rFillColor)
sal_Int32 getSystemFont(const Font &i_rFont)
void addDocumentAttachedFile(OUString const &rFileName, OUString const &rMimeType, OUString const &rDescription, std::unique_ptr< PDFOutputStream > rStream)
std::vector< PDFScreen > m_aScreens
Contains all screen annotations.
static const char * getAttributeValueTag(PDFWriter::StructAttributeValue eVal)
void drawShadow(SalLayout &rLayout, const OUString &rText, bool bTextLines)
void writeJPG(const JPGEmit &rEmit)
bool prepareEncryption(const css::uno::Reference< css::beans::XMaterialHolder > &)
bool appendDest(sal_Int32 nDestID, OStringBuffer &rBuffer)
std::vector< PDFNoteEntry > m_aNotes
void setPageTransition(PDFWriter::PageTransition eType, sal_uInt32 nMilliSec, sal_Int32 nPageNr)
void MARK(const char *pString)
void emitComment(const char *pComment)
sal_Int32 createToUnicodeCMap(sal_uInt8 const *pEncoding, const std::vector< sal_Ucs > &CodeUnits, const sal_Int32 *pCodeUnitsPerGlyph, const sal_Int32 *pEncToUnicodeIndex, uint32_t nGlyphs)
ExternalPDFStreams m_aExternalPDFStreams
sal_Int32 registerDestReference(sal_Int32 nDestId, const tools::Rectangle &rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType)
sal_Int32 createOutlineItem(sal_Int32 nParent, std::u16string_view rText, sal_Int32 nDestID)
void emitTextAnnotationLine(OStringBuffer &aLine, PDFNoteEntry const &rNote)
sal_Int32 getResourceDictObj()
ResourceDict m_aGlobalResourceDict
sal_Int32 createDest(const tools::Rectangle &rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType)
bool emitAppearances(PDFWidget &rWidget, OStringBuffer &rAnnotDict)
void intersectClipRegion(const tools::Rectangle &rRect)
void setLineColor(const Color &rColor)
void setLinkDest(sal_Int32 nLinkId, sal_Int32 nDestId)
static void pushResource(ResourceKind eKind, const OString &rResource, sal_Int32 nObject, ResourceDict &rResourceDict, std::list< StreamRedirect > &rOutputStreams)
void updateGraphicsState(Mode mode=Mode::DEFAULT)
css::uno::Reference< css::util::XURLTransformer > m_xTrans
void registerGlyph(const sal_GlyphId, const vcl::font::PhysicalFontFace *, const LogicalFontInstance *pFont, const std::vector< sal_Ucs > &, sal_Int32, sal_uInt8 &, sal_Int32 &)
void drawWaveTextLine(OStringBuffer &aLine, tools::Long nWidth, FontLineStyle eTextLine, Color aColor, bool bIsAbove)
std::vector< PDFWidget > m_aWidgets
static bool compressStream(SvMemoryStream *)
sal_uInt64 getCurrentFilePosition()
void setClipRegion(const basegfx::B2DPolyPolygon &rRegion)
sal_Int32 emitNamedDestinations()
bool writeBitmapObject(const BitmapEmit &rObject, bool bMask=false)
static const sal_uInt8 s_nPadString[ENCRYPTED_PWD_SIZE]
bool emitType3Font(const vcl::font::PhysicalFontFace *, const FontSubset &, std::map< sal_Int32, sal_Int32 > &)
::std::map< sal_Int32, sal_Int32 > m_aDestinationIdTranslation
contains destinations accessible via a public Id, instead of being linked to by an ordinary link
Font replaceFont(const Font &rControlFont, const Font &rAppSetFont)
SvStream * endRedirect()
void drawPolygon(const tools::Polygon &rPoly)
std::vector< PDFOutlineEntry > m_aOutline
std::unique_ptr< ZCodec > m_pCodec
void registerSimpleGlyph(const sal_GlyphId, const vcl::font::PhysicalFontFace *, const std::vector< sal_Ucs > &, sal_Int32, sal_uInt8 &, sal_Int32 &)
void drawPixel(const Point &rPt, const Color &rColor)
static void AppendUnicodeTextString(const OUString &rString, OStringBuffer &rBuffer)
Write rString as a PDF hex string into rBuffer.
static OString GetDateTime()
Get current date/time in PDF D:YYYYMMDDHHMMSS form.
@ Warning_Transparency_Omitted_PDFA
Definition: pdfwriter.hxx:219
@ Warning_Transparency_Omitted_PDF13
Definition: pdfwriter.hxx:223
@ Warning_FormAction_Omitted_PDFA
Definition: pdfwriter.hxx:227
bool read(BitmapEx &rBitmap)
Array object: a list.
SvMemoryStream & GetEditBuffer()
Access to the input document, even after the input stream is gone.
Name object: a key string.
Numbering object: an integer or a real.
Indirect object: something with a unique ID.
Definition: pdfdocument.hxx:69
PDFElement * Lookup(const OString &rDictionaryKey)
PDFObjectElement * LookupObject(const OString &rDictionaryKey)
Reference object: something with a unique ID.
tools::Long GetYOffset() const
tools::Rectangle GetRect1() const
tools::PolyPolygon GetShape() const
tools::Rectangle GetRect2() const
tools::Long GetWidth() const
bool IsShapePolyLine() const
abstract base class for physical font faces
RawFontData GetGlyphColorBitmap(sal_GlyphId, tools::Rectangle &) const
bool CreateFontSubset(std::vector< sal_uInt8 > &, const sal_GlyphId *, const sal_uInt8 *, const int, FontSubsetInfo &) const
std::vector< ColorLayer > GetGlyphColorLayers(sal_GlyphId) const
virtual const std::vector< hb_variation_t > & GetVariations(const LogicalFontInstance &) const
const std::vector< ColorPalette > & GetColorPalettes() const
RawFontData GetRawFontData(uint32_t) const
OString GetGlyphName(sal_GlyphId, bool=false) const
const BuildinFont & GetBuildinFont() const
static const BuildinFont & Get(int nId)
void addCode(sal_Ucs i_cCode)
void setGlyphWidth(sal_Int32 nWidth)
void setGlyphId(sal_uInt8 i_nId)
void translate(double tx, double ty)
Definition: Matrix3.cxx:85
Point transform(const Point &rPoint) const
Definition: Matrix3.cxx:27
void rotate(double angle)
Definition: Matrix3.cxx:71
double get(size_t i) const
Definition: Matrix3.hxx:46
void scale(double sx, double sy)
Definition: Matrix3.cxx:59
void skew(double alpha, double beta)
Definition: Matrix3.cxx:39
const void * getData()
constexpr ::Color COL_WHITE(0xFF, 0xFF, 0xFF)
constexpr ::Color COL_LIGHTGRAY(0xC0, 0xC0, 0xC0)
constexpr ::Color COL_BLACK(0x00, 0x00, 0x00)
constexpr ::Color COL_TRANSPARENT(ColorTransparency, 0xFF, 0xFF, 0xFF, 0xFF)
#define MAX_SIGNATURE_CONTENT_LENGTH
int nCount
Reference< XComponentContext > m_aContext
double toRadians(D x)
URL aURL
float u
float y
float x
EmbeddedObjectRef * pObject
std::deque< AttacherIndex_Impl > aIndex
DocumentType eType
sal_Int16 nValue
FontRelief
Definition: fntstyle.hxx:26
FontLineStyle
LINESTYLE_BOLDDASHDOT
LINESTYLE_BOLDDASHDOTDOT
LINESTYLE_SINGLE
LINESTYLE_BOLDLONGDASH
LINESTYLE_BOLDWAVE
LINESTYLE_DASHDOTDOT
LINESTYLE_BOLDDASH
LINESTYLE_BOLDDOTTED
LINESTYLE_DOUBLEWAVE
LINESTYLE_DOUBLE
LINESTYLE_NONE
LINESTYLE_DASH
LINESTYLE_DONTKNOW
LINESTYLE_SMALLWAVE
LINESTYLE_DASHDOT
LINESTYLE_DOTTED
LINESTYLE_WAVE
LINESTYLE_LONGDASH
LINESTYLE_BOLD
FontStrikeout
STRIKEOUT_BOLD
STRIKEOUT_DOUBLE
STRIKEOUT_SINGLE
STRIKEOUT_X
STRIKEOUT_SLASH
STRIKEOUT_NONE
STRIKEOUT_DONTKNOW
PITCH_FIXED
ITALIC_NORMAL
ITALIC_DONTKNOW
ITALIC_OBLIQUE
FontEmphasisMark
ALIGN_BOTTOM
ALIGN_TOP
FAMILY_SCRIPT
FAMILY_ROMAN
WEIGHT_DONTKNOW
WEIGHT_MEDIUM
@ TYPE1_PFB
PSType1 Postscript Font Binary.
@ CFF_FONT
CFF-container with PSType2 glyphs.
@ TYPE1_PFA
PSType1 Postscript Font Ascii.
@ SFNT_TTF
SFNT container with TrueType glyphs.
uint32_t sal_GlyphId
Definition: glyphid.hxx:24
tools::Long FRound(double fVal)
sal_Int32 nIndex
OUString aName
sal_Int64 n
uno_Any a
LINEEND_LF
TOOLS_DLLPUBLIC OString convertLineEnd(const OString &rIn, LineEnd eLineEnd)
sal_uInt16 nPos
#define SAL_WARN_IF(condition, area, stream)
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
MapUnit
aStr
aBuf
constexpr OUStringLiteral aData
SVL_DLLPUBLIC OUString resolveIdnaHost(OUString const &url)
bool equal(T const &rfValA, T const &rfValB)
B2DPolyPolygon prepareForPolygonOperation(const B2DPolygon &rCandidate)
B2DPolygon createPolygonFromRect(const B2DRectangle &rRect, double fRadiusX, double fRadiusY)
void applyLineDashing(const B2DPolygon &rCandidate, const std::vector< double > &rDotDashArray, B2DPolyPolygon *pLineTarget, B2DPolyPolygon *pGapTarget, double fDotDashLength)
B2DPolyPolygon solvePolygonOperationAnd(const B2DPolyPolygon &rCandidateA, const B2DPolyPolygon &rCandidateB)
B2DPolygon adaptiveSubdivideByAngle(const B2DPolygon &rCandidate, double fAngleBound)
bool isRectangle(const B2DPolygon &rPoly)
constexpr double deg2rad(double v)
OUString encodeForXml(std::u16string_view rStr)
OStringBuffer & padToLength(OStringBuffer &rBuffer, sal_Int32 nLength, char cFill='\0')
Reference< XComponentContext > getProcessComponentContext()
HashType
int i
m
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
constexpr T & temporary(T &&x)
group
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
OUString WhitespaceToSpace(std::u16string_view rLine, bool bProtect)
Definition: strhelper.cxx:254
Regular
TextAlign
css::uno::Reference< css::linguistic2::XProofreadingIterator > get(css::uno::Reference< css::uno::XComponentContext > const &context)
long Long
double getDefaultPdfResolutionDpi()
Get the default PDF rendering resolution in DPI.
Definition: PdfConfig.cxx:20
double pointToPixel(const double fPoint, const double fResolutionDPI)
Convert to inch, then assume 96 DPI.
Definition: pdfcompat.hxx:21
constexpr sal_Int32 g_nInheritedPageWidth
constexpr sal_Int32 g_nInheritedPageHeight
ComplexTextLayoutFlags
Definition: State.hxx:76
basegfx::B2DRange b2DRectangleFromRectangle(const ::tools::Rectangle &rRect)
static void appendStructureAttributeLine(PDFWriter::StructAttribute i_eAttr, const PDFStructureAttribute &i_rVal, OStringBuffer &o_rLine, bool i_bIsFixedInt)
PushFlags
Definition: State.hxx:40
static void lcl_assignMeta(std::u16string_view aValue, OString &aMeta)
static int XUnits(int unitsPerEm, int n)
Definition: sft.cxx:205
int ThreeInts[3]
static void appendSubsetName(int nSubsetID, std::u16string_view rPSName, OStringBuffer &rBuffer)
static OString GenerateID(sal_Int32 const nObjectId)
static bool getPfbSegmentLengths(const unsigned char *pFontBytes, int nByteLen, ThreeInts &rSegmentLengths)
static const Color & replaceColor(const Color &rCol1, const Color &rCol2)
static double calcAngle(const tools::Rectangle &rRect, const Point &rPoint)
constexpr sal_uInt16 pixelFormatBitCount(PixelFormat ePixelFormat)
Definition: BitmapTypes.hxx:34
HashMap_OWString_Interface aMap
@ OUTDEV_PDF
Definition: outdev.hxx:145
#define Y
#define CHECK_RETURN(x)
#define CHECK_RETURN2(x)
#define HCONV(x)
static bool g_bDebugDisableCompression
constexpr sal_Int32 MAXIMUM_RC4_KEY_LENGTH
constexpr sal_Int32 ENCRYPTED_PWD_SIZE
PolyFlags
sal_Int32 attribute
ConversionMode mode
sal_Unicode sal_Ucs
Definition: salgdi.hxx:64
DeviceFormat
Definition: salgtype.hxx:28
sal_uIntPtr sal_uLong
#define STREAM_SEEK_TO_END
#define STREAM_SEEK_TO_BEGIN
ImplSVGDIData maGDIData
Definition: svdata.hxx:398
std::shared_ptr< vcl::font::PhysicalFontCollection > mxScreenFontList
Definition: svdata.hxx:225
std::shared_ptr< ImplFontCache > mxScreenFontCache
Definition: svdata.hxx:226
std::shared_ptr< filter::PDFDocument > & getPDFDocument()
Color annotColor
Definition: pdfwriter.hxx:71
std::vector< basegfx::B2DPolygon > maPolygons
Definition: pdfwriter.hxx:70
OUString Contents
Definition: pdfwriter.hxx:67
OUString Title
Definition: pdfwriter.hxx:66
bool isFreeText
Definition: pdfwriter.hxx:69
Color interiorColor
Definition: pdfwriter.hxx:72
css::util::DateTime maModificationDate
Definition: pdfwriter.hxx:68
tools::Rectangle Location
Definition: pdfwriter.hxx:246
WidgetType getType() const
Definition: pdfwriter.hxx:284
std::vector< OUString > Entries
Definition: pdfwriter.hxx:450
std::vector< double > m_aDashArray
Definition: pdfwriter.hxx:100
std::vector< OUString > Entries
Definition: pdfwriter.hxx:429
std::vector< sal_Int32 > SelectedEntries
Definition: pdfwriter.hxx:430
std::vector< sal_uInt8 > UValue
Definition: pdfwriter.hxx:535
std::vector< sal_uInt8 > DocumentIdentifier
Definition: pdfwriter.hxx:537
std::vector< sal_uInt8 > EncryptionKey
Definition: pdfwriter.hxx:536
std::vector< sal_uInt8 > OValue
Definition: pdfwriter.hxx:534
PDFWriter::PDFLinkDefaultAction DefaultLinkAction
Definition: pdfwriter.hxx:580
PDFWriter::ExportDataFormat SubmitFormat
Definition: pdfwriter.hxx:600
css::uno::Reference< css::security::XCertificate > SignCertificate
Definition: pdfwriter.hxx:640
bool UseReferenceXObject
Use reference XObject markup for PDF images.
Definition: pdfwriter.hxx:643
PDFWriter::PDFViewerPageMode PDFDocumentMode
Definition: pdfwriter.hxx:606
PDFWriter::PDFViewerAction PDFDocumentAction
Definition: pdfwriter.hxx:607
PDFWriter::PDFEncryptionProperties Encryption
Definition: pdfwriter.hxx:628
PDFWriter::PDFDocInfo DocumentInfo
Definition: pdfwriter.hxx:629
PDFWriter::ColorMode ColorMode
Definition: pdfwriter.hxx:639
css::lang::Locale DocumentLocale
Definition: pdfwriter.hxx:636
ReferenceXObjectEmit m_aReferenceXObject
BitmapChecksum m_nChecksum
Definition: BitmapID.hxx:23
BitmapChecksum m_nMaskChecksum
Definition: BitmapID.hxx:24
sal_Int32 m_nSize
Definition: BitmapID.hxx:22
rtl_TextEncoding const m_eCharSet
OString getNameObject() const
LogicalFontInstance * m_pFontInstance
std::map< sal_GlyphId, GlyphEmit > m_aMapping
std::vector< FontEmit > m_aSubsets
std::map< sal_GlyphId, Glyph > m_aMapping
sal_uInt8 m_nSubsetGlyphID
vcl::text::ComplexTextLayoutFlags m_nLayoutMode
basegfx::B2DPolyPolygon m_aClipRegion
GraphicsStateUpdateFlags m_nUpdateFlags
std::unique_ptr< SvMemoryStream > m_pStream
ReferenceXObjectEmit m_aReferenceXObject
tools::Rectangle m_aRect
PDFWriter::DestAreaType m_eType
tools::Rectangle m_aRect
PDFWriter::DestAreaType m_eType
tools::Rectangle m_aRect
PDFPopupAnnotation m_aPopUpAnnotation
std::vector< sal_Int32 > m_aChildren
PDFPage(PDFWriterImpl *pWriter, double nPageWidth, double nPageHeight, PDFWriter::Orientation eOrientation)
void appendWaveLine(sal_Int32 nLength, sal_Int32 nYOffset, sal_Int32 nDelta, OStringBuffer &rBuffer) const
sal_uInt64 m_nBeginStreamPos
void appendPoint(const Point &rPoint, OStringBuffer &rBuffer) const
void appendPixelPoint(const basegfx::B2DPoint &rPoint, OStringBuffer &rBuffer) const
void convertRect(tools::Rectangle &rRect) const
VclPtr< PDFWriterImpl > m_pWriter
void appendMatrix3(Matrix3 const &rMatrix, OStringBuffer &rBuffer)
std::vector< sal_Int32 > m_aAnnotations
void appendRect(const tools::Rectangle &rRect, OStringBuffer &rBuffer) const
sal_Int32 m_nUserUnit
A positive number that gives the size of default user space units, in multiples of points.
bool appendLineInfo(const LineInfo &rInfo, OStringBuffer &rBuffer) const
std::vector< sal_Int32 > m_aMCIDParents
double getHeight() const
void appendMappedLength(sal_Int32 nLength, OStringBuffer &rBuffer, bool bVertical=true, sal_Int32 *pOutLength=nullptr) const
sal_Int32 m_nStreamLengthObject
bool emit(sal_Int32 nParentPage)
void appendPolygon(const tools::Polygon &rPoly, OStringBuffer &rBuffer, bool bClose=true) const
PDFWriter::Orientation m_eOrientation
std::vector< sal_Int32 > m_aStreamObjects
void appendPolyPolygon(const tools::PolyPolygon &rPolyPoly, OStringBuffer &rBuffer) const
PDFWriter::PageTransition m_eTransition
sal_Int32 m_nParentObject
ID of the parent object.
A PDF Screen annotation.
OUString m_aTempFileURL
Embedded video.
sal_Int32 m_nTempFileObject
ID of the EmbeddedFile object.
OUString m_aURL
Linked video.
OUString m_AltText
alternative text description
PDFWriter::StructAttributeValue eValue
::std::vector< sal_Int32 > m_AnnotIds
std::map< PDFWriter::StructAttribute, PDFStructureAttribute > m_aAttributes
std::vector< sal_Int32 > m_aChildren
std::list< PDFStructureElementKid > m_aKids
::std::optional< PDFWriter::StructElement > m_oType
DrawTextFlags m_nTextStyle
std::unordered_map< OString, PDFAppearanceStreams > m_aAppearances
PDFWriter::WidgetType m_eType
std::vector< sal_Int32 > m_aKidsIndex
std::vector< sal_Int32 > m_aKids
PDFWriter::FormatType m_nFormat
std::vector< OUString > m_aListEntries
std::vector< sal_Int32 > m_aSelectedEntries
Contains information to emit a reference XObject.
sal_Int32 m_nEmbeddedObject
ID of the vector/embedded object, if m_nFormObject is used.
sal_Int32 m_nFormObject
ID of the Form XObject, if any.
sal_Int32 getObject() const
Returns the ID one should use when referring to this bitmap.
sal_Int32 m_nBitmapObject
ID of the bitmap object, if m_nFormObject is used.
sal_Int32 m_nExternalPDFDataIndex
PDF data from the graphic object, if not writing a reference XObject.
Size m_aPixelSize
Size of the bitmap replacement, in pixels.
std::map< OString, sal_Int32 > m_aPatterns
std::map< OString, sal_Int32 > m_aExtGStates
void append(OStringBuffer &rBuffer, sal_Int32 nFontDictObject)
std::map< OString, sal_Int32 > m_aShadings
std::map< OString, sal_Int32 > m_aXObjects
std::unique_ptr< SvMemoryStream > m_pContentStream
ImplSVData * ImplGetSVData()
Definition: svdata.cxx:77
OUString removeMnemonicFromString(OUString const &rStr)
unsigned char sal_uInt8
#define SAL_MAX_INT32
#define SAL_MIN_INT32
sal_uInt16 sal_Unicode
signed char sal_Int8
OUString aTargetURL
INetProtocol
sal_uInt32 sal_UCS4
Definition: vclenum.hxx:160
std::unique_ptr< char[]> aBuffer
sal_Int32 nLength