Coverage Report

Created: 2026-08-14 10:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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: */