/src/libreoffice/vcl/source/pdf/PDFPage.cxx
Line | Count | Source |
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/types.h> |
21 | | #include <sal/log.hxx> |
22 | | #include <osl/file.hxx> |
23 | | #include <rtl/strbuf.hxx> |
24 | | #include <rtl/string.hxx> |
25 | | #include <tools/gen.hxx> |
26 | | #include <tools/long.hxx> |
27 | | #include <tools/poly.hxx> |
28 | | #include <basegfx/point/b2dpoint.hxx> |
29 | | #include <basegfx/polygon/b2dpolygon.hxx> |
30 | | #include <basegfx/polygon/b2dpolygontools.hxx> |
31 | | #include <basegfx/polygon/b2dpolypolygon.hxx> |
32 | | #include <basegfx/range/b2drange.hxx> |
33 | | #include <basegfx/vector/b2enums.hxx> |
34 | | |
35 | | #include <vcl/lineinfo.hxx> |
36 | | #include <vcl/pdfwriter.hxx> |
37 | | |
38 | | #include <pdf/Matrix3.hxx> |
39 | | #include <pdf/pdfwriter_impl.hxx> |
40 | | |
41 | | #include "pdfwriter_utils.hxx" |
42 | | |
43 | | #include <vector> |
44 | | |
45 | | using namespace::com::sun::star; |
46 | | |
47 | | static bool g_bDebugDisableCompression = getenv("VCL_DEBUG_DISABLE_PDFCOMPRESSION"); |
48 | | |
49 | | namespace vcl |
50 | | { |
51 | | |
52 | | PDFPage::PDFPage( PDFWriterImpl* pWriter, double nPageWidth, double nPageHeight, PDFWriter::Orientation eOrientation ) |
53 | | : |
54 | 62.5k | m_pWriter( pWriter ), |
55 | 62.5k | m_nPageWidth( nPageWidth ), |
56 | 62.5k | m_nPageHeight( nPageHeight ), |
57 | 62.5k | m_nUserUnit( 1 ), |
58 | 62.5k | m_eOrientation( eOrientation ), |
59 | 62.5k | m_nPageObject( 0 ), // invalid object number |
60 | 62.5k | m_nStreamLengthObject( 0 ), |
61 | 62.5k | m_nBeginStreamPos( 0 ), |
62 | 62.5k | m_eTransition( PDFWriter::PageTransition::Regular ), |
63 | 62.5k | m_nTransTime( 0 ) |
64 | 62.5k | { |
65 | | // object ref must be only ever updated in emit() |
66 | 62.5k | m_nPageObject = m_pWriter->createObject(); |
67 | | |
68 | 62.5k | switch (m_pWriter->m_aContext.Version) |
69 | 62.5k | { |
70 | | // 1.6 or later |
71 | 62.5k | default: |
72 | 62.5k | m_nUserUnit = std::ceil(std::max(nPageWidth, nPageHeight) / 14400.0); |
73 | 62.5k | break; |
74 | 0 | case PDFWriter::PDFVersion::PDF_1_4: |
75 | 0 | case PDFWriter::PDFVersion::PDF_1_5: |
76 | 0 | case PDFWriter::PDFVersion::PDF_A_1: |
77 | 0 | break; |
78 | 62.5k | } |
79 | 62.5k | } |
80 | | |
81 | | void PDFPage::beginStream() |
82 | 62.5k | { |
83 | 62.5k | if (g_bDebugDisableCompression) |
84 | 0 | { |
85 | 0 | m_pWriter->emitComment("PDFWriterImpl::PDFPage::beginStream, +"); |
86 | 0 | } |
87 | 62.5k | m_aStreamObjects.push_back(m_pWriter->createObject()); |
88 | 62.5k | if( ! m_pWriter->updateObject( m_aStreamObjects.back() ) ) |
89 | 0 | return; |
90 | | |
91 | 62.5k | m_nStreamLengthObject = m_pWriter->createObject(); |
92 | | // write content stream header |
93 | 62.5k | OStringBuffer aLine( |
94 | 62.5k | OString::number(m_aStreamObjects.back()) |
95 | 62.5k | + " 0 obj\n<</Length " |
96 | 62.5k | + OString::number( m_nStreamLengthObject ) |
97 | 62.5k | + " 0 R" ); |
98 | 62.5k | if (!g_bDebugDisableCompression) |
99 | 62.5k | aLine.append( "/Filter/FlateDecode" ); |
100 | 62.5k | aLine.append( ">>\nstream\n" ); |
101 | 62.5k | if( ! m_pWriter->writeBuffer( aLine ) ) |
102 | 0 | return; |
103 | 62.5k | if (osl::File::E_None != m_pWriter->m_aFile.getPos(m_nBeginStreamPos)) |
104 | 0 | { |
105 | 0 | m_pWriter->m_aFile.close(); |
106 | 0 | m_pWriter->m_bOpen = false; |
107 | 0 | } |
108 | 62.5k | if (!g_bDebugDisableCompression) |
109 | 62.5k | m_pWriter->beginCompression(); |
110 | 62.5k | m_pWriter->checkAndEnableStreamEncryption( m_aStreamObjects.back() ); |
111 | 62.5k | } |
112 | | |
113 | | void PDFPage::endStream() |
114 | 62.5k | { |
115 | 62.5k | if (!g_bDebugDisableCompression) |
116 | 62.5k | m_pWriter->endCompression(); |
117 | 62.5k | sal_uInt64 nEndStreamPos; |
118 | 62.5k | if (osl::File::E_None != m_pWriter->m_aFile.getPos(nEndStreamPos)) |
119 | 0 | { |
120 | 0 | m_pWriter->m_aFile.close(); |
121 | 0 | m_pWriter->m_bOpen = false; |
122 | 0 | return; |
123 | 0 | } |
124 | 62.5k | m_pWriter->disableStreamEncryption(); |
125 | 62.5k | if( ! m_pWriter->writeBuffer( "\nendstream\nendobj\n\n" ) ) |
126 | 0 | return; |
127 | | // emit stream length object |
128 | 62.5k | if( ! m_pWriter->updateObject( m_nStreamLengthObject ) ) |
129 | 0 | return; |
130 | 62.5k | OString aLine = |
131 | 62.5k | OString::number( m_nStreamLengthObject ) + |
132 | 62.5k | " 0 obj\n" + |
133 | 62.5k | OString::number( static_cast<sal_Int64>(nEndStreamPos-m_nBeginStreamPos) ) + |
134 | 62.5k | "\nendobj\n\n"; |
135 | 62.5k | m_pWriter->writeBuffer( aLine ); |
136 | 62.5k | } |
137 | | |
138 | | bool PDFPage::emit(sal_Int32 nParentObject ) |
139 | 62.5k | { |
140 | 62.5k | m_pWriter->MARK("PDFPage::emit"); |
141 | | // emit page object |
142 | 62.5k | if( ! m_pWriter->updateObject( m_nPageObject ) ) |
143 | 0 | return false; |
144 | 62.5k | OStringBuffer aLine( |
145 | 62.5k | OString::number(m_nPageObject) |
146 | 62.5k | + " 0 obj\n" |
147 | 62.5k | "<</Type/Page/Parent " |
148 | 62.5k | + OString::number(nParentObject) |
149 | 62.5k | + " 0 R" |
150 | 62.5k | "/Resources " |
151 | 62.5k | + OString::number(m_pWriter->getResourceDictObj()) |
152 | 62.5k | + " 0 R" ); |
153 | 62.5k | if( m_nPageWidth && m_nPageHeight ) |
154 | 62.5k | { |
155 | 62.5k | aLine.append( "/MediaBox[0 0 " |
156 | 62.5k | + OString::number(m_nPageWidth / m_nUserUnit) |
157 | 62.5k | + " " |
158 | 62.5k | + OString::number(m_nPageHeight / m_nUserUnit) |
159 | 62.5k | + "]" ); |
160 | 62.5k | if (m_nUserUnit > 1) |
161 | 14 | { |
162 | 14 | aLine.append("\n/UserUnit " + OString::number(m_nUserUnit)); |
163 | 14 | } |
164 | 62.5k | } |
165 | 62.5k | switch( m_eOrientation ) |
166 | 62.5k | { |
167 | 0 | case PDFWriter::Orientation::Portrait: aLine.append( "/Rotate 0\n" );break; |
168 | 62.5k | case PDFWriter::Orientation::Inherit: break; |
169 | 62.5k | } |
170 | 62.5k | int nAnnots = m_aAnnotations.size(); |
171 | 62.5k | if( nAnnots > 0 ) |
172 | 923 | { |
173 | 923 | aLine.append( "/Annots[\n" ); |
174 | 2.83k | for( int i = 0; i < nAnnots; i++ ) |
175 | 1.91k | { |
176 | 1.91k | aLine.append( OString::number(m_aAnnotations[i]) |
177 | 1.91k | + " 0 R" ); |
178 | 1.91k | aLine.append( ((i+1)%15) ? " " : "\n" ); |
179 | 1.91k | } |
180 | 923 | aLine.append( "]\n" ); |
181 | 923 | } |
182 | 62.5k | if (PDFWriter::PDFVersion::PDF_1_5 <= m_pWriter->m_aContext.Version |
183 | 0 | || m_pWriter->m_aContext.UniversalAccessibilityCompliance) |
184 | 62.5k | { |
185 | | // ISO 14289-1:2014, Clause: 7.18.3 requires it if there are annotations |
186 | | // but Adobe Acrobat Pro complains if it is ever missing so just |
187 | | // write it always. |
188 | | // PDF/UA-1 mandates structural tab order on every page that holds |
189 | | // annotations, so emit /Tabs /S even when targeting PDF 1.4 (PDF/A-1) |
190 | | // when PDF/UA compliance is also requested. |
191 | 62.5k | aLine.append( "/Tabs/S\n" ); |
192 | 62.5k | } |
193 | 62.5k | if( !m_aMCIDParents.empty() ) |
194 | 0 | { |
195 | 0 | OStringBuffer aStructParents( 1024 ); |
196 | 0 | aStructParents.append( "[ " ); |
197 | 0 | int nParents = m_aMCIDParents.size(); |
198 | 0 | for( int i = 0; i < nParents; i++ ) |
199 | 0 | { |
200 | 0 | aStructParents.append( OString::number(m_aMCIDParents[i]) |
201 | 0 | + " 0 R" ); |
202 | 0 | aStructParents.append( ((i%10) == 9) ? "\n" : " " ); |
203 | 0 | } |
204 | 0 | aStructParents.append( "]" ); |
205 | 0 | m_pWriter->m_aStructParentTree.push_back( aStructParents.makeStringAndClear() ); |
206 | |
|
207 | 0 | aLine.append( "/StructParents " |
208 | 0 | + OString::number( sal_Int32(m_pWriter->m_aStructParentTree.size()-1) ) |
209 | 0 | + "\n" ); |
210 | 0 | } |
211 | 62.5k | if( m_eTransition != PDFWriter::PageTransition::Regular && m_nTransTime > 0 ) |
212 | 0 | { |
213 | | // transition duration |
214 | 0 | aLine.append( "/Trans<</D " ); |
215 | 0 | appendDouble( static_cast<double>(m_nTransTime)/1000.0, aLine, 3 ); |
216 | 0 | aLine.append( "\n" ); |
217 | 0 | const char *pStyle = nullptr, *pDm = nullptr, *pM = nullptr, *pDi = nullptr; |
218 | 0 | switch( m_eTransition ) |
219 | 0 | { |
220 | 0 | case PDFWriter::PageTransition::SplitHorizontalInward: |
221 | 0 | pStyle = "Split"; pDm = "H"; pM = "I"; break; |
222 | 0 | case PDFWriter::PageTransition::SplitHorizontalOutward: |
223 | 0 | pStyle = "Split"; pDm = "H"; pM = "O"; break; |
224 | 0 | case PDFWriter::PageTransition::SplitVerticalInward: |
225 | 0 | pStyle = "Split"; pDm = "V"; pM = "I"; break; |
226 | 0 | case PDFWriter::PageTransition::SplitVerticalOutward: |
227 | 0 | pStyle = "Split"; pDm = "V"; pM = "O"; break; |
228 | 0 | case PDFWriter::PageTransition::BlindsHorizontal: |
229 | 0 | pStyle = "Blinds"; pDm = "H"; break; |
230 | 0 | case PDFWriter::PageTransition::BlindsVertical: |
231 | 0 | pStyle = "Blinds"; pDm = "V"; break; |
232 | 0 | case PDFWriter::PageTransition::BoxInward: |
233 | 0 | pStyle = "Box"; pM = "I"; break; |
234 | 0 | case PDFWriter::PageTransition::BoxOutward: |
235 | 0 | pStyle = "Box"; pM = "O"; break; |
236 | 0 | case PDFWriter::PageTransition::WipeLeftToRight: |
237 | 0 | pStyle = "Wipe"; pDi = "0"; break; |
238 | 0 | case PDFWriter::PageTransition::WipeBottomToTop: |
239 | 0 | pStyle = "Wipe"; pDi = "90"; break; |
240 | 0 | case PDFWriter::PageTransition::WipeRightToLeft: |
241 | 0 | pStyle = "Wipe"; pDi = "180"; break; |
242 | 0 | case PDFWriter::PageTransition::WipeTopToBottom: |
243 | 0 | pStyle = "Wipe"; pDi = "270"; break; |
244 | 0 | case PDFWriter::PageTransition::Dissolve: |
245 | 0 | pStyle = "Dissolve"; break; |
246 | 0 | case PDFWriter::PageTransition::Regular: |
247 | 0 | break; |
248 | 0 | } |
249 | | // transition style |
250 | 0 | if( pStyle ) |
251 | 0 | { |
252 | 0 | aLine.append( OString::Concat("/S/") + pStyle + "\n" ); |
253 | 0 | } |
254 | 0 | if( pDm ) |
255 | 0 | { |
256 | 0 | aLine.append( OString::Concat("/Dm/") + pDm + "\n" ); |
257 | 0 | } |
258 | 0 | if( pM ) |
259 | 0 | { |
260 | 0 | aLine.append( OString::Concat("/M/") + pM + "\n" ); |
261 | 0 | } |
262 | 0 | if( pDi ) |
263 | 0 | { |
264 | 0 | aLine.append( OString::Concat("/Di ") + pDi + "\n" ); |
265 | 0 | } |
266 | 0 | aLine.append( ">>\n" ); |
267 | 0 | } |
268 | | |
269 | 62.5k | aLine.append( "/Contents" ); |
270 | 62.5k | unsigned int nStreamObjects = m_aStreamObjects.size(); |
271 | 62.5k | if( nStreamObjects > 1 ) |
272 | 0 | aLine.append( '[' ); |
273 | 62.5k | for(sal_Int32 i : m_aStreamObjects) |
274 | 62.5k | { |
275 | 62.5k | aLine.append( " " + OString::number( i ) + " 0 R" ); |
276 | 62.5k | } |
277 | 62.5k | if( nStreamObjects > 1 ) |
278 | 0 | aLine.append( ']' ); |
279 | 62.5k | aLine.append( ">>\nendobj\n\n" ); |
280 | 62.5k | return m_pWriter->writeBuffer( aLine ); |
281 | 62.5k | } |
282 | | |
283 | | void PDFPage::appendPoint( const Point& rPoint, OStringBuffer& rBuffer ) const |
284 | 581k | { |
285 | 581k | Point aPoint( convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, |
286 | 581k | m_pWriter->m_aMapMode, |
287 | 581k | m_pWriter, |
288 | 581k | rPoint ) ); |
289 | | |
290 | 581k | sal_Int32 nValue = aPoint.X(); |
291 | | |
292 | 581k | appendFixedInt( nValue, rBuffer ); |
293 | | |
294 | 581k | rBuffer.append( ' ' ); |
295 | | |
296 | 581k | nValue = pointToPixel(getHeight()) - aPoint.Y(); |
297 | | |
298 | 581k | appendFixedInt( nValue, rBuffer ); |
299 | 581k | } |
300 | | |
301 | | void PDFPage::appendPixelPoint( const basegfx::B2DPoint& rPoint, OStringBuffer& rBuffer ) const |
302 | 63.1k | { |
303 | 63.1k | double fValue = pixelToPoint(rPoint.getX()); |
304 | | |
305 | 63.1k | appendDouble( fValue, rBuffer, nLog10Divisor ); |
306 | 63.1k | rBuffer.append( ' ' ); |
307 | 63.1k | fValue = getHeight() - pixelToPoint(rPoint.getY()); |
308 | 63.1k | appendDouble( fValue, rBuffer, nLog10Divisor ); |
309 | 63.1k | } |
310 | | |
311 | | void PDFPage::appendRect( const tools::Rectangle& rRect, OStringBuffer& rBuffer ) const |
312 | 618 | { |
313 | 618 | appendPoint( rRect.BottomLeft() + Point( 0, 1 ), rBuffer ); |
314 | 618 | rBuffer.append( ' ' ); |
315 | 618 | appendMappedLength( static_cast<sal_Int32>(rRect.GetWidth()), rBuffer, false ); |
316 | 618 | rBuffer.append( ' ' ); |
317 | 618 | appendMappedLength( static_cast<sal_Int32>(rRect.GetHeight()), rBuffer ); |
318 | 618 | rBuffer.append( " re" ); |
319 | 618 | } |
320 | | |
321 | | void PDFPage::convertRect( tools::Rectangle& rRect ) const |
322 | 4.44k | { |
323 | 4.44k | const Point aLL = convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, |
324 | 4.44k | m_pWriter->m_aMapMode, |
325 | 4.44k | m_pWriter, |
326 | 4.44k | rRect.BottomLeft() + Point( 0, 1 ) |
327 | 4.44k | ); |
328 | 4.44k | const Size aSize = convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, |
329 | 4.44k | m_pWriter->m_aMapMode, |
330 | 4.44k | m_pWriter, |
331 | 4.44k | rRect.GetSize() ); |
332 | 4.44k | rRect.SetLeft( aLL.X() ); |
333 | 4.44k | rRect.SetRight( aLL.X() + aSize.Width() ); |
334 | 4.44k | rRect.SetTop( pointToPixel(getHeight()) - aLL.Y() ); |
335 | 4.44k | rRect.SetBottom( rRect.Top() + aSize.Height() ); |
336 | 4.44k | } |
337 | | |
338 | | void PDFPage::appendPolygon( const tools::Polygon& rPoly, OStringBuffer& rBuffer, bool bClose ) const |
339 | 15.1k | { |
340 | 15.1k | const sal_uInt16 nPoints = rPoly.GetSize(); |
341 | | /* |
342 | | * #108582# applications do weird things |
343 | | */ |
344 | 15.1k | sal_uInt32 nBufLen = rBuffer.getLength(); |
345 | 15.1k | if( nPoints <= 0 ) |
346 | 0 | return; |
347 | | |
348 | 15.1k | const PolyFlags* pFlagArray = rPoly.GetConstFlagAry(); |
349 | 15.1k | appendPoint( rPoly[0], rBuffer ); |
350 | 15.1k | rBuffer.append( " m\n" ); |
351 | 40.2k | for( sal_uInt16 i = 1; i < nPoints; i++ ) |
352 | 25.1k | { |
353 | 25.1k | if( pFlagArray && pFlagArray[i] == PolyFlags::Control && nPoints-i > 2 ) |
354 | 0 | { |
355 | | // bezier |
356 | 0 | SAL_WARN_IF( pFlagArray[i+1] != PolyFlags::Control || pFlagArray[i+2] == PolyFlags::Control, "vcl.pdfwriter", "unexpected sequence of control points" ); |
357 | 0 | appendPoint( rPoly[i], rBuffer ); |
358 | 0 | rBuffer.append( " " ); |
359 | 0 | appendPoint( rPoly[i+1], rBuffer ); |
360 | 0 | rBuffer.append( " " ); |
361 | 0 | appendPoint( rPoly[i+2], rBuffer ); |
362 | 0 | rBuffer.append( " c" ); |
363 | 0 | i += 2; // add additionally consumed points |
364 | 0 | } |
365 | 25.1k | else |
366 | 25.1k | { |
367 | | // line |
368 | 25.1k | appendPoint( rPoly[i], rBuffer ); |
369 | 25.1k | rBuffer.append( " l" ); |
370 | 25.1k | } |
371 | 25.1k | if( (rBuffer.getLength() - nBufLen) > 65 ) |
372 | 3.35k | { |
373 | 3.35k | rBuffer.append( "\n" ); |
374 | 3.35k | nBufLen = rBuffer.getLength(); |
375 | 3.35k | } |
376 | 21.8k | else |
377 | 21.8k | rBuffer.append( " " ); |
378 | 25.1k | } |
379 | 15.1k | if( bClose ) |
380 | 3.35k | rBuffer.append( "h\n" ); |
381 | 15.1k | } |
382 | | |
383 | | void PDFPage::appendPolygon( const basegfx::B2DPolygon& rPoly, OStringBuffer& rBuffer ) const |
384 | 63.1k | { |
385 | 63.1k | basegfx::B2DPolygon aPoly( convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, |
386 | 63.1k | m_pWriter->m_aMapMode, |
387 | 63.1k | m_pWriter, |
388 | 63.1k | rPoly ) ); |
389 | | |
390 | 63.1k | if( basegfx::utils::isRectangle( aPoly ) ) |
391 | 63.1k | { |
392 | 63.1k | basegfx::B2DRange aRange( aPoly.getB2DRange() ); |
393 | 63.1k | basegfx::B2DPoint aBL( aRange.getMinX(), aRange.getMaxY() ); |
394 | 63.1k | appendPixelPoint( aBL, rBuffer ); |
395 | 63.1k | rBuffer.append( ' ' ); |
396 | 63.1k | appendMappedLength( aRange.getWidth(), rBuffer, false, nLog10Divisor ); |
397 | 63.1k | rBuffer.append( ' ' ); |
398 | 63.1k | appendMappedLength( aRange.getHeight(), rBuffer, true, nLog10Divisor ); |
399 | 63.1k | rBuffer.append( " re\n" ); |
400 | 63.1k | return; |
401 | 63.1k | } |
402 | 0 | sal_uInt32 nPoints = aPoly.count(); |
403 | 0 | if( nPoints <= 0 ) |
404 | 0 | return; |
405 | | |
406 | 0 | sal_uInt32 nBufLen = rBuffer.getLength(); |
407 | 0 | basegfx::B2DPoint aLastPoint( aPoly.getB2DPoint( 0 ) ); |
408 | 0 | appendPixelPoint( aLastPoint, rBuffer ); |
409 | 0 | rBuffer.append( " m\n" ); |
410 | 0 | for( sal_uInt32 i = 1; i <= nPoints; i++ ) |
411 | 0 | { |
412 | 0 | if( i != nPoints || aPoly.isClosed() ) |
413 | 0 | { |
414 | 0 | sal_uInt32 nCurPoint = i % nPoints; |
415 | 0 | sal_uInt32 nLastPoint = i-1; |
416 | 0 | basegfx::B2DPoint aPoint( aPoly.getB2DPoint( nCurPoint ) ); |
417 | 0 | if( aPoly.isNextControlPointUsed( nLastPoint ) && |
418 | 0 | aPoly.isPrevControlPointUsed( nCurPoint ) ) |
419 | 0 | { |
420 | 0 | appendPixelPoint( aPoly.getNextControlPoint( nLastPoint ), rBuffer ); |
421 | 0 | rBuffer.append( ' ' ); |
422 | 0 | appendPixelPoint( aPoly.getPrevControlPoint( nCurPoint ), rBuffer ); |
423 | 0 | rBuffer.append( ' ' ); |
424 | 0 | appendPixelPoint( aPoint, rBuffer ); |
425 | 0 | rBuffer.append( " c" ); |
426 | 0 | } |
427 | 0 | else if( aPoly.isNextControlPointUsed( nLastPoint ) ) |
428 | 0 | { |
429 | 0 | appendPixelPoint( aPoly.getNextControlPoint( nLastPoint ), rBuffer ); |
430 | 0 | rBuffer.append( ' ' ); |
431 | 0 | appendPixelPoint( aPoint, rBuffer ); |
432 | 0 | rBuffer.append( " y" ); |
433 | 0 | } |
434 | 0 | else if( aPoly.isPrevControlPointUsed( nCurPoint ) ) |
435 | 0 | { |
436 | 0 | appendPixelPoint( aPoly.getPrevControlPoint( nCurPoint ), rBuffer ); |
437 | 0 | rBuffer.append( ' ' ); |
438 | 0 | appendPixelPoint( aPoint, rBuffer ); |
439 | 0 | rBuffer.append( " v" ); |
440 | 0 | } |
441 | 0 | else |
442 | 0 | { |
443 | 0 | appendPixelPoint( aPoint, rBuffer ); |
444 | 0 | rBuffer.append( " l" ); |
445 | 0 | } |
446 | 0 | if( (rBuffer.getLength() - nBufLen) > 65 ) |
447 | 0 | { |
448 | 0 | rBuffer.append( "\n" ); |
449 | 0 | nBufLen = rBuffer.getLength(); |
450 | 0 | } |
451 | 0 | else |
452 | 0 | rBuffer.append( " " ); |
453 | 0 | } |
454 | 0 | } |
455 | 0 | rBuffer.append( "h\n" ); |
456 | 0 | } |
457 | | |
458 | | void PDFPage::appendPolyPolygon( const tools::PolyPolygon& rPolyPoly, OStringBuffer& rBuffer ) const |
459 | 3.34k | { |
460 | 3.34k | sal_uInt16 nPolygons = rPolyPoly.Count(); |
461 | 6.69k | for( sal_uInt16 n = 0; n < nPolygons; n++ ) |
462 | 3.34k | appendPolygon( rPolyPoly[n], rBuffer ); |
463 | 3.34k | } |
464 | | |
465 | | void PDFPage::appendPolyPolygon( const basegfx::B2DPolyPolygon& rPolyPoly, OStringBuffer& rBuffer ) const |
466 | 63.1k | { |
467 | 63.1k | for(auto const& rPolygon : rPolyPoly) |
468 | 63.1k | appendPolygon( rPolygon, rBuffer ); |
469 | 63.1k | } |
470 | | |
471 | | void PDFPage::appendMappedLength( sal_Int32 nLength, OStringBuffer& rBuffer, bool bVertical, sal_Int32* pOutLength ) const |
472 | 541k | { |
473 | 541k | sal_Int32 nValue = nLength; |
474 | 541k | if ( nLength < 0 ) |
475 | 240 | { |
476 | 240 | rBuffer.append( '-' ); |
477 | 240 | nValue = -nLength; |
478 | 240 | } |
479 | 541k | Size aSize( convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, |
480 | 541k | m_pWriter->m_aMapMode, |
481 | 541k | m_pWriter, |
482 | 541k | Size( nValue, nValue ) ) ); |
483 | 541k | nValue = bVertical ? aSize.Height() : aSize.Width(); |
484 | 541k | if( pOutLength ) |
485 | 0 | *pOutLength = ((nLength < 0 ) ? -nValue : nValue); |
486 | | |
487 | 541k | appendFixedInt( nValue, rBuffer ); |
488 | 541k | } |
489 | | |
490 | | void PDFPage::appendMappedLength( double fLength, OStringBuffer& rBuffer, bool bVertical, sal_Int32 nPrecision ) const |
491 | 138k | { |
492 | 138k | Size aSize( convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, |
493 | 138k | m_pWriter->m_aMapMode, |
494 | 138k | m_pWriter, |
495 | 138k | Size( 1000, 1000 ) ) ); |
496 | 138k | fLength *= pixelToPoint(static_cast<double>(bVertical ? aSize.Height() : aSize.Width()) / 1000.0); |
497 | 138k | appendDouble( fLength, rBuffer, nPrecision ); |
498 | 138k | } |
499 | | |
500 | | bool PDFPage::appendLineInfo( const LineInfo& rInfo, OStringBuffer& rBuffer ) const |
501 | 0 | { |
502 | 0 | if(LineStyle::Dash == rInfo.GetStyle() && rInfo.GetDashLen() != rInfo.GetDotLen()) |
503 | 0 | { |
504 | | // dashed and non-degraded case, check for implementation limits of dash array |
505 | | // in PDF reader apps (e.g. acroread) |
506 | 0 | if(2 * (rInfo.GetDashCount() + rInfo.GetDotCount()) > 10) |
507 | 0 | { |
508 | 0 | return false; |
509 | 0 | } |
510 | 0 | } |
511 | | |
512 | 0 | if(basegfx::B2DLineJoin::NONE != rInfo.GetLineJoin()) |
513 | 0 | { |
514 | | // LineJoin used, ExtLineInfo required |
515 | 0 | return false; |
516 | 0 | } |
517 | | |
518 | 0 | if(css::drawing::LineCap_BUTT != rInfo.GetLineCap()) |
519 | 0 | { |
520 | | // LineCap used, ExtLineInfo required |
521 | 0 | return false; |
522 | 0 | } |
523 | | |
524 | 0 | if( rInfo.GetStyle() == LineStyle::Dash ) |
525 | 0 | { |
526 | 0 | rBuffer.append( "[ " ); |
527 | 0 | if( rInfo.GetDashLen() == rInfo.GetDotLen() ) // degraded case |
528 | 0 | { |
529 | 0 | appendMappedLength( rInfo.GetDashLen(), rBuffer ); |
530 | 0 | rBuffer.append( ' ' ); |
531 | 0 | appendMappedLength( rInfo.GetDistance(), rBuffer ); |
532 | 0 | rBuffer.append( ' ' ); |
533 | 0 | } |
534 | 0 | else |
535 | 0 | { |
536 | 0 | for( int n = 0; n < rInfo.GetDashCount(); n++ ) |
537 | 0 | { |
538 | 0 | appendMappedLength( rInfo.GetDashLen(), rBuffer ); |
539 | 0 | rBuffer.append( ' ' ); |
540 | 0 | appendMappedLength( rInfo.GetDistance(), rBuffer ); |
541 | 0 | rBuffer.append( ' ' ); |
542 | 0 | } |
543 | 0 | for( int m = 0; m < rInfo.GetDotCount(); m++ ) |
544 | 0 | { |
545 | 0 | appendMappedLength( rInfo.GetDotLen(), rBuffer ); |
546 | 0 | rBuffer.append( ' ' ); |
547 | 0 | appendMappedLength( rInfo.GetDistance(), rBuffer ); |
548 | 0 | rBuffer.append( ' ' ); |
549 | 0 | } |
550 | 0 | } |
551 | 0 | rBuffer.append( "] 0 d\n" ); |
552 | 0 | } |
553 | |
|
554 | 0 | if( rInfo.GetWidth() > 1 ) |
555 | 0 | { |
556 | 0 | appendMappedLength( rInfo.GetWidth(), rBuffer ); |
557 | 0 | rBuffer.append( " w\n" ); |
558 | 0 | } |
559 | 0 | else if( rInfo.GetWidth() == 0 ) |
560 | 0 | { |
561 | | // "pixel" line |
562 | 0 | appendDouble( 72.0/double(m_pWriter->GetDPIX()), rBuffer ); |
563 | 0 | rBuffer.append( " w\n" ); |
564 | 0 | } |
565 | |
|
566 | 0 | return true; |
567 | 0 | } |
568 | | |
569 | | void PDFPage::appendWaveLine( sal_Int32 nWidth, sal_Int32 nY, sal_Int32 nDelta, OStringBuffer& rBuffer ) const |
570 | 0 | { |
571 | 0 | if( nWidth <= 0 ) |
572 | 0 | return; |
573 | 0 | if( nDelta < 1 ) |
574 | 0 | nDelta = 1; |
575 | |
|
576 | 0 | rBuffer.append( "0 " ); |
577 | 0 | appendMappedLength( nY, rBuffer ); |
578 | 0 | rBuffer.append( " m\n" ); |
579 | 0 | for( sal_Int32 n = 0; n < nWidth; ) |
580 | 0 | { |
581 | 0 | n += nDelta; |
582 | 0 | appendMappedLength( n, rBuffer, false ); |
583 | 0 | rBuffer.append( ' ' ); |
584 | 0 | appendMappedLength( nDelta+nY, rBuffer ); |
585 | 0 | rBuffer.append( ' ' ); |
586 | 0 | n += nDelta; |
587 | 0 | appendMappedLength( n, rBuffer, false ); |
588 | 0 | rBuffer.append( ' ' ); |
589 | 0 | appendMappedLength( nY, rBuffer ); |
590 | 0 | rBuffer.append( " v " ); |
591 | 0 | if( n < nWidth ) |
592 | 0 | { |
593 | 0 | n += nDelta; |
594 | 0 | appendMappedLength( n, rBuffer, false ); |
595 | 0 | rBuffer.append( ' ' ); |
596 | 0 | appendMappedLength( nY-nDelta, rBuffer ); |
597 | 0 | rBuffer.append( ' ' ); |
598 | 0 | n += nDelta; |
599 | 0 | appendMappedLength( n, rBuffer, false ); |
600 | 0 | rBuffer.append( ' ' ); |
601 | 0 | appendMappedLength( nY, rBuffer ); |
602 | 0 | rBuffer.append( " v\n" ); |
603 | 0 | } |
604 | 0 | } |
605 | 0 | rBuffer.append( "S\n" ); |
606 | 0 | } |
607 | | |
608 | | void PDFPage::appendMatrix3(Matrix3 const & rMatrix, OStringBuffer& rBuffer) |
609 | 36.5k | { |
610 | 36.5k | appendDouble(rMatrix.get(0), rBuffer); |
611 | 36.5k | rBuffer.append(' '); |
612 | 36.5k | appendDouble(rMatrix.get(1), rBuffer); |
613 | 36.5k | rBuffer.append(' '); |
614 | 36.5k | appendDouble(rMatrix.get(2), rBuffer); |
615 | 36.5k | rBuffer.append(' '); |
616 | 36.5k | appendDouble(rMatrix.get(3), rBuffer); |
617 | 36.5k | rBuffer.append(' '); |
618 | 36.5k | appendPoint(Point(tools::Long(rMatrix.get(4)), tools::Long(rMatrix.get(5))), rBuffer); |
619 | 36.5k | } |
620 | | |
621 | | double PDFPage::getHeight() const |
622 | 648k | { |
623 | 648k | double fRet = m_nPageHeight ? m_nPageHeight : 842; // default A4 height in inch/72, OK to use hardcoded value here? |
624 | | |
625 | 648k | if (m_nUserUnit > 1) |
626 | 0 | { |
627 | 0 | fRet /= m_nUserUnit; |
628 | 0 | } |
629 | | |
630 | 648k | return fRet; |
631 | 648k | } |
632 | | |
633 | | } |
634 | | |
635 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |