Bug Summary

File:home/maarten/src/libreoffice/core/sdext/source/pdfimport/tree/pdfiprocessor.cxx
Warning:line 460, column 1
Potential leak of memory pointed to by 'pLink'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name pdfiprocessor.cxx -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -D BOOST_ERROR_CODE_HEADER_ONLY -D BOOST_SYSTEM_NO_DEPRECATED -D CPPU_ENV=gcc3 -D LINUX -D OSL_DEBUG_LEVEL=1 -D SAL_LOG_INFO -D SAL_LOG_WARN -D UNIX -D UNX -D X86_64 -D _PTHREADS -D _REENTRANT -D SYSTEM_ZLIB -D BOOST_ALL_NO_LIB -D EXCEPTIONS_ON -D LIBO_INTERNAL_ONLY -I /home/maarten/src/libreoffice/core/external/boost/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/boost -I /home/maarten/src/libreoffice/core/sdext/source/pdfimport/inc -I /home/maarten/src/libreoffice/core/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include/linux -I /home/maarten/src/libreoffice/core/config_host -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/udkapi/normal -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/offapi/normal -I /home/maarten/src/libreoffice/core/workdir/CustomTarget/sdext/pdfimport -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/libxml2 -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10 -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/x86_64-redhat-linux -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/backward -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O0 -Wno-missing-braces -std=c++17 -fdeprecated-macro -fdebug-compilation-dir /home/maarten/src/libreoffice/core -ferror-limit 19 -fvisibility hidden -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcxx-exceptions -fexceptions -debug-info-kind=constructor -analyzer-output=html -faddrsig -o /home/maarten/tmp/wis/scan-build-libreoffice/output/report/2020-10-07-141433-9725-1 -x c++ /home/maarten/src/libreoffice/core/sdext/source/pdfimport/tree/pdfiprocessor.cxx

/home/maarten/src/libreoffice/core/sdext/source/pdfimport/tree/pdfiprocessor.cxx

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
21#include <pdfiprocessor.hxx>
22#include <xmlemitter.hxx>
23#include <pdfihelper.hxx>
24#include <imagecontainer.hxx>
25#include <genericelements.hxx>
26#include "style.hxx"
27#include <treevisiting.hxx>
28
29#include <sal/log.hxx>
30
31#include <comphelper/sequence.hxx>
32#include <basegfx/polygon/b2dpolygonclipper.hxx>
33#include <basegfx/polygon/b2dpolygontools.hxx>
34#include <basegfx/utils/canvastools.hxx>
35#include <basegfx/matrix/b2dhommatrix.hxx>
36#include <vcl/svapp.hxx>
37
38using namespace com::sun::star;
39
40
41namespace pdfi
42{
43
44 PDFIProcessor::PDFIProcessor( const uno::Reference< task::XStatusIndicator >& xStat ,
45 css::uno::Reference< css::uno::XComponentContext > const & xContext) :
46
47 m_xContext(xContext),
48 prevCharWidth(0),
49 m_pDocument( ElementFactory::createDocumentElement() ),
50 m_pCurPage(nullptr),
51 m_pCurElement(nullptr),
52 m_nNextFontId( 1 ),
53 m_aIdToFont(),
54 m_aFontToId(),
55 m_aGCStack(),
56 m_nNextGCId( 1 ),
57 m_aIdToGC(),
58 m_aGCToId(),
59 m_aImages(),
60 m_nPages(0),
61 m_nNextZOrder( 1 ),
62 m_xStatusIndicator( xStat )
63{
64 FontAttributes aDefFont;
65 aDefFont.familyName = "Helvetica";
66 aDefFont.isBold = false;
67 aDefFont.isItalic = false;
68 aDefFont.size = 10*PDFI_OUTDEV_RESOLUTION7200/72;
69 m_aIdToFont.insert({0, aDefFont});
70 m_aFontToId.insert({aDefFont, 0});
71
72 GraphicsContext aDefGC;
73 m_aGCStack.push_back( aDefGC );
74 m_aGCToId.insert({aDefGC, 0});
75 m_aIdToGC.insert({0, aDefGC});
76}
77
78void PDFIProcessor::setPageNum( sal_Int32 nPages )
79{
80 m_nPages = nPages;
81}
82
83
84void PDFIProcessor::pushState()
85{
86 GraphicsContextStack::value_type const a(m_aGCStack.back());
87 m_aGCStack.push_back(a);
88}
89
90void PDFIProcessor::popState()
91{
92 m_aGCStack.pop_back();
93}
94
95void PDFIProcessor::setFlatness( double value )
96{
97 getCurrentContext().Flatness = value;
98}
99
100void PDFIProcessor::setTransformation( const geometry::AffineMatrix2D& rMatrix )
101{
102 basegfx::unotools::homMatrixFromAffineMatrix(
103 getCurrentContext().Transformation,
104 rMatrix );
105}
106
107void PDFIProcessor::setLineDash( const uno::Sequence<double>& dashes,
108 double /*start*/ )
109{
110 // TODO(F2): factor in start offset
111 GraphicsContext& rContext( getCurrentContext() );
112 comphelper::sequenceToContainer(rContext.DashArray,dashes);
113}
114
115void PDFIProcessor::setLineJoin(sal_Int8 nJoin)
116{
117 getCurrentContext().LineJoin = nJoin;
118}
119
120void PDFIProcessor::setLineCap(sal_Int8 nCap)
121{
122 getCurrentContext().LineCap = nCap;
123}
124
125void PDFIProcessor::setMiterLimit(double)
126{
127 SAL_WARN("sdext.pdfimport", "PDFIProcessor::setMiterLimit(): not supported by ODF")do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "sdext.pdfimport")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break
; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail::getResult
( ::sal::detail::StreamStart() << "PDFIProcessor::setMiterLimit(): not supported by ODF"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sdext.pdfimport"
), ("/home/maarten/src/libreoffice/core/sdext/source/pdfimport/tree/pdfiprocessor.cxx"
":" "127" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "PDFIProcessor::setMiterLimit(): not supported by ODF"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "PDFIProcessor::setMiterLimit(): not supported by ODF"
; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sdext.pdfimport"
), ("/home/maarten/src/libreoffice/core/sdext/source/pdfimport/tree/pdfiprocessor.cxx"
":" "127" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "PDFIProcessor::setMiterLimit(): not supported by ODF"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sdext.pdfimport"
), ("/home/maarten/src/libreoffice/core/sdext/source/pdfimport/tree/pdfiprocessor.cxx"
":" "127" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "PDFIProcessor::setMiterLimit(): not supported by ODF"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "PDFIProcessor::setMiterLimit(): not supported by ODF"
; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sdext.pdfimport"
), ("/home/maarten/src/libreoffice/core/sdext/source/pdfimport/tree/pdfiprocessor.cxx"
":" "127" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
128}
129
130void PDFIProcessor::setLineWidth(double nWidth)
131{
132 getCurrentContext().LineWidth = nWidth;
133}
134
135void PDFIProcessor::setFillColor( const rendering::ARGBColor& rColor )
136{
137 getCurrentContext().FillColor = rColor;
138}
139
140void PDFIProcessor::setStrokeColor( const rendering::ARGBColor& rColor )
141{
142 getCurrentContext().LineColor = rColor;
143}
144
145void PDFIProcessor::setFont( const FontAttributes& i_rFont )
146{
147 FontAttributes aChangedFont( i_rFont );
148 GraphicsContext& rGC=getCurrentContext();
149 // for text render modes, please see PDF reference manual
150 aChangedFont.isOutline = ( (rGC.TextRenderMode == 1) || (rGC. TextRenderMode == 2) );
151 FontToIdMap::const_iterator it = m_aFontToId.find( aChangedFont );
152 if( it != m_aFontToId.end() )
153 rGC.FontId = it->second;
154 else
155 {
156 m_aFontToId[ aChangedFont ] = m_nNextFontId;
157 m_aIdToFont[ m_nNextFontId ] = aChangedFont;
158 rGC.FontId = m_nNextFontId;
159 m_nNextFontId++;
160 }
161}
162
163void PDFIProcessor::setTextRenderMode( sal_Int32 i_nMode )
164{
165 GraphicsContext& rGC=getCurrentContext();
166 rGC.TextRenderMode = i_nMode;
167 IdToFontMap::iterator it = m_aIdToFont.find( rGC.FontId );
168 if( it != m_aIdToFont.end() )
169 setFont( it->second );
170}
171
172sal_Int32 PDFIProcessor::getFontId( const FontAttributes& rAttr ) const
173{
174 const sal_Int32 nCurFont = getCurrentContext().FontId;
175 const_cast<PDFIProcessor*>(this)->setFont( rAttr );
176 const sal_Int32 nFont = getCurrentContext().FontId;
177 const_cast<PDFIProcessor*>(this)->getCurrentContext().FontId = nCurFont;
178
179 return nFont;
180}
181
182// line diagnose block - start
183void PDFIProcessor::processGlyphLine()
184{
185 if (m_GlyphsList.empty())
186 return;
187
188 double spaceDetectBoundary = 0.0;
189
190 // Try to find space glyph and its width
191 for (CharGlyph & i : m_GlyphsList)
192 {
193 OUString& glyph = i.getGlyph();
194
195 sal_Unicode ch = '\0';
196 if (!glyph.isEmpty())
197 ch = glyph[0];
198
199 if ((ch == 0x20) || (ch == 0xa0))
200 {
201 double spaceWidth = i.getWidth();
202 spaceDetectBoundary = spaceWidth * 0.5;
203 break;
204 }
205 }
206
207 // If space glyph is not found, use average glyph width instead
208 if (spaceDetectBoundary == 0.0)
209 {
210 double avgGlyphWidth = 0.0;
211 for (const CharGlyph & i : m_GlyphsList)
212 avgGlyphWidth += i.getWidth();
213 avgGlyphWidth /= m_GlyphsList.size();
214 spaceDetectBoundary = avgGlyphWidth * 0.2;
215 }
216
217 FrameElement* frame = ElementFactory::createFrameElement(
218 m_GlyphsList[0].getCurElement(),
219 getGCId(m_GlyphsList[0].getGC()));
220 frame->ZOrder = m_nNextZOrder++;
221 frame->IsForText = true;
222 frame->FontSize = getFont(m_GlyphsList[0].getGC().FontId).size;
223 ParagraphElement* para = ElementFactory::createParagraphElement(frame);
224
225 for (size_t i = 0; i < m_GlyphsList.size(); i++)
226 {
227 bool prependSpace = false;
228 TextElement* text = ElementFactory::createTextElement(
229 para,
230 getGCId(m_GlyphsList[i].getGC()),
231 m_GlyphsList[i].getGC().FontId);
232 if (i == 0)
233 {
234 text->x = m_GlyphsList[0].getGC().Transformation.get(0, 2);
235 text->y = m_GlyphsList[0].getGC().Transformation.get(1, 2);
236 text->w = 0;
237 text->h = 0;
238 para->updateGeometryWith(text);
239 frame->updateGeometryWith(para);
240 }
241 else
242 {
243 double spaceSize = m_GlyphsList[i].getPrevSpaceWidth();
244 prependSpace = spaceSize > spaceDetectBoundary;
245 }
246 if (prependSpace)
247 text->Text.append(" ");
248 text->Text.append(m_GlyphsList[i].getGlyph());
249 }
250
251 m_GlyphsList.clear();
252}
253
254void PDFIProcessor::drawGlyphs( const OUString& rGlyphs,
255 const geometry::RealRectangle2D& rRect,
256 const geometry::Matrix2D& rFontMatrix,
257 double fontSize)
258{
259 double ascent = getFont(getCurrentContext().FontId).ascent;
260
261 basegfx::B2DHomMatrix fontMatrix(
262 rFontMatrix.m00, rFontMatrix.m01, 0.0,
263 rFontMatrix.m10, rFontMatrix.m11, 0.0);
264 fontMatrix.scale(fontSize, fontSize);
265
266 basegfx::B2DHomMatrix totalTextMatrix1(fontMatrix);
267 basegfx::B2DHomMatrix totalTextMatrix2(fontMatrix);
268 totalTextMatrix1.translate(rRect.X1, rRect.Y1);
269 totalTextMatrix2.translate(rRect.X2, rRect.Y2);
270
271 basegfx::B2DHomMatrix corrMatrix;
272 corrMatrix.scale(1.0, -1.0);
273 corrMatrix.translate(0.0, ascent);
274 totalTextMatrix1 = totalTextMatrix1 * corrMatrix;
275 totalTextMatrix2 = totalTextMatrix2 * corrMatrix;
276
277 totalTextMatrix1 *= getCurrentContext().Transformation;
278 totalTextMatrix2 *= getCurrentContext().Transformation;
279
280 basegfx::B2DHomMatrix invMatrix(totalTextMatrix1);
281 basegfx::B2DHomMatrix invPrevMatrix(prevTextMatrix);
282 invMatrix.invert();
283 invPrevMatrix.invert();
284 basegfx::B2DHomMatrix offsetMatrix1(totalTextMatrix1);
285 basegfx::B2DHomMatrix offsetMatrix2(totalTextMatrix2);
286 offsetMatrix1 *= invPrevMatrix;
287 offsetMatrix2 *= invMatrix;
288
289 double charWidth = offsetMatrix2.get(0, 2);
290 double prevSpaceWidth = offsetMatrix1.get(0, 2) - prevCharWidth;
291
292 if ((totalTextMatrix1.get(0, 0) != prevTextMatrix.get(0, 0)) ||
293 (totalTextMatrix1.get(0, 1) != prevTextMatrix.get(0, 1)) ||
294 (totalTextMatrix1.get(1, 0) != prevTextMatrix.get(1, 0)) ||
295 (totalTextMatrix1.get(1, 1) != prevTextMatrix.get(1, 1)) ||
296 (offsetMatrix1.get(0, 2) < 0.0) ||
297 (prevSpaceWidth > prevCharWidth * 1.3) ||
298 (!basegfx::fTools::equalZero(offsetMatrix1.get(1, 2), 0.0001)))
299 {
300 processGlyphLine();
301 }
302
303 CharGlyph aGlyph(m_pCurElement, getCurrentContext(), charWidth, prevSpaceWidth, rGlyphs);
304 aGlyph.getGC().Transformation = totalTextMatrix1;
305 m_GlyphsList.push_back(aGlyph);
306
307 prevCharWidth = charWidth;
308 prevTextMatrix = totalTextMatrix1;
309}
310
311void PDFIProcessor::endText()
312{
313 TextElement* pText = dynamic_cast<TextElement*>(m_pCurElement);
314 if( pText )
315 m_pCurElement = pText->Parent;
316}
317
318void PDFIProcessor::setupImage(ImageId nImage)
319{
320 const GraphicsContext& rGC(getCurrentContext());
321
322 basegfx::B2DTuple aScale, aTranslation;
323 double fRotate, fShearX;
324 rGC.Transformation.decompose(aScale, aTranslation, fRotate, fShearX);
325
326 const sal_Int32 nGCId = getGCId(rGC);
327 FrameElement* pFrame = ElementFactory::createFrameElement( m_pCurElement, nGCId );
328 ImageElement* pImageElement = ElementFactory::createImageElement( pFrame, nGCId, nImage );
329 pFrame->x = pImageElement->x = aTranslation.getX();
330 pFrame->y = pImageElement->y = aTranslation.getY();
331 pFrame->w = pImageElement->w = aScale.getX();
332 pFrame->h = pImageElement->h = aScale.getY();
333 pFrame->ZOrder = m_nNextZOrder++;
334
335 // Poppler wrapper takes into account that vertical axes of PDF and ODF are opposite,
336 // and it flips matrix vertically (see poppler's GfxState::GfxState()).
337 // But image internal vertical axis is independent of PDF vertical axis direction,
338 // so arriving matrix is extra-flipped relative to image.
339 // We force vertical flip here to compensate that.
340 pFrame->MirrorVertical = true;
341}
342
343void PDFIProcessor::drawMask(const uno::Sequence<beans::PropertyValue>& xBitmap,
344 bool /*bInvert*/ )
345{
346 // TODO(F3): Handle mask and inversion
347 setupImage( m_aImages.addImage(xBitmap) );
348}
349
350void PDFIProcessor::drawImage(const uno::Sequence<beans::PropertyValue>& xBitmap )
351{
352 setupImage( m_aImages.addImage(xBitmap) );
353}
354
355void PDFIProcessor::drawColorMaskedImage(const uno::Sequence<beans::PropertyValue>& xBitmap,
356 const uno::Sequence<uno::Any>& /*xMaskColors*/ )
357{
358 // TODO(F3): Handle mask colors
359 setupImage( m_aImages.addImage(xBitmap) );
360}
361
362void PDFIProcessor::drawMaskedImage(const uno::Sequence<beans::PropertyValue>& xBitmap,
363 const uno::Sequence<beans::PropertyValue>& /*xMask*/,
364 bool /*bInvertMask*/)
365{
366 // TODO(F3): Handle mask and inversion
367 setupImage( m_aImages.addImage(xBitmap) );
368}
369
370void PDFIProcessor::drawAlphaMaskedImage(const uno::Sequence<beans::PropertyValue>& xBitmap,
371 const uno::Sequence<beans::PropertyValue>& /*xMask*/)
372{
373 // TODO(F3): Handle mask
374
375 setupImage( m_aImages.addImage(xBitmap) );
376
377}
378
379void PDFIProcessor::strokePath( const uno::Reference< rendering::XPolyPolygon2D >& rPath )
380{
381 basegfx::B2DPolyPolygon aPoly=basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(rPath);
382 aPoly.transform(getCurrentContext().Transformation);
383
384 PolyPolyElement* pPoly = ElementFactory::createPolyPolyElement(
385 m_pCurElement,
386 getGCId(getCurrentContext()),
387 aPoly,
388 PATH_STROKE );
389 pPoly->updateGeometry();
390 pPoly->ZOrder = m_nNextZOrder++;
391}
392
393void PDFIProcessor::fillPath( const uno::Reference< rendering::XPolyPolygon2D >& rPath )
394{
395 basegfx::B2DPolyPolygon aPoly=basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(rPath);
396 aPoly.transform(getCurrentContext().Transformation);
397
398 PolyPolyElement* pPoly = ElementFactory::createPolyPolyElement(
399 m_pCurElement,
400 getGCId(getCurrentContext()),
401 aPoly,
402 PATH_FILL );
403 pPoly->updateGeometry();
404 pPoly->ZOrder = m_nNextZOrder++;
405}
406
407void PDFIProcessor::eoFillPath( const uno::Reference< rendering::XPolyPolygon2D >& rPath )
408{
409 basegfx::B2DPolyPolygon aPoly=basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(rPath);
410 aPoly.transform(getCurrentContext().Transformation);
411
412 PolyPolyElement* pPoly = ElementFactory::createPolyPolyElement(
413 m_pCurElement,
414 getGCId(getCurrentContext()),
415 aPoly,
416 PATH_EOFILL );
417 pPoly->updateGeometry();
418 pPoly->ZOrder = m_nNextZOrder++;
419}
420
421void PDFIProcessor::intersectClip(const uno::Reference< rendering::XPolyPolygon2D >& rPath)
422{
423 // TODO(F3): interpret fill mode
424 basegfx::B2DPolyPolygon aNewClip = basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(rPath);
425 aNewClip.transform(getCurrentContext().Transformation);
426 basegfx::B2DPolyPolygon aCurClip = getCurrentContext().Clip;
427
428 if( aCurClip.count() ) // #i92985# adapted API from (..., false, false) to (..., true, false)
429 aNewClip = basegfx::utils::clipPolyPolygonOnPolyPolygon( aCurClip, aNewClip, true, false );
430
431 getCurrentContext().Clip = aNewClip;
432}
433
434void PDFIProcessor::intersectEoClip(const uno::Reference< rendering::XPolyPolygon2D >& rPath)
435{
436 // TODO(F3): interpret fill mode
437 basegfx::B2DPolyPolygon aNewClip = basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(rPath);
438 aNewClip.transform(getCurrentContext().Transformation);
439 basegfx::B2DPolyPolygon aCurClip = getCurrentContext().Clip;
440
441 if( aCurClip.count() ) // #i92985# adapted API from (..., false, false) to (..., true, false)
442 aNewClip = basegfx::utils::clipPolyPolygonOnPolyPolygon( aCurClip, aNewClip, true, false );
443
444 getCurrentContext().Clip = aNewClip;
445}
446
447void PDFIProcessor::hyperLink( const geometry::RealRectangle2D& rBounds,
448 const OUString& rURI )
449{
450 if( !rURI.isEmpty() )
1
Taking true branch
451 {
452 HyperlinkElement* pLink = ElementFactory::createHyperlinkElement(
2
Calling 'ElementFactory::createHyperlinkElement'
4
Returned allocated memory
453 &m_pCurPage->Hyperlinks,
454 rURI );
455 pLink->x = rBounds.X1;
456 pLink->y = rBounds.Y1;
457 pLink->w = rBounds.X2-rBounds.X1;
458 pLink->h = rBounds.Y2-rBounds.Y1;
459 }
460}
5
Potential leak of memory pointed to by 'pLink'
461
462const FontAttributes& PDFIProcessor::getFont( sal_Int32 nFontId ) const
463{
464 IdToFontMap::const_iterator it = m_aIdToFont.find( nFontId );
465 if( it == m_aIdToFont.end() )
466 it = m_aIdToFont.find( 0 );
467 return it->second;
468}
469
470sal_Int32 PDFIProcessor::getGCId( const GraphicsContext& rGC )
471{
472 sal_Int32 nGCId = 0;
473 auto it = m_aGCToId.find( rGC );
474 if( it != m_aGCToId.end() )
475 nGCId = it->second;
476 else
477 {
478 m_aGCToId.insert({rGC, m_nNextGCId});
479 m_aIdToGC.insert({m_nNextGCId, rGC});
480 nGCId = m_nNextGCId;
481 m_nNextGCId++;
482 }
483
484 return nGCId;
485}
486
487const GraphicsContext& PDFIProcessor::getGraphicsContext( sal_Int32 nGCId ) const
488{
489 auto it = m_aIdToGC.find( nGCId );
490 if( it == m_aIdToGC.end() )
491 it = m_aIdToGC.find( 0 );
492 return it->second;
493}
494
495void PDFIProcessor::endPage()
496{
497 processGlyphLine(); // draw last line
498 if( m_xStatusIndicator.is()
499 && m_pCurPage
500 && m_pCurPage->PageNumber == m_nPages
501 )
502 m_xStatusIndicator->end();
503}
504
505void PDFIProcessor::startPage( const geometry::RealSize2D& rSize )
506{
507 // initial clip is to page bounds
508 getCurrentContext().Clip = basegfx::B2DPolyPolygon(
509 basegfx::utils::createPolygonFromRect(
510 basegfx::B2DRange( 0, 0, rSize.Width, rSize.Height )));
511
512 sal_Int32 nNextPageNr = m_pCurPage ? m_pCurPage->PageNumber+1 : 1;
513 if( m_xStatusIndicator.is() )
514 {
515 if( nNextPageNr == 1 )
516 startIndicator( " " );
517 m_xStatusIndicator->setValue( nNextPageNr );
518 }
519 m_pCurPage = ElementFactory::createPageElement(m_pDocument.get(), nNextPageNr);
520 m_pCurElement = m_pCurPage;
521 m_pCurPage->w = rSize.Width;
522 m_pCurPage->h = rSize.Height;
523 m_nNextZOrder = 1;
524
525
526}
527
528void PDFIProcessor::emit( XmlEmitter& rEmitter,
529 const TreeVisitorFactory& rVisitorFactory )
530{
531#if OSL_DEBUG_LEVEL1 > 0
532 m_pDocument->emitStructure( 0 );
533#endif
534
535 ElementTreeVisitorSharedPtr optimizingVisitor(
536 rVisitorFactory.createOptimizingVisitor(*this));
537 // FIXME: localization
538 startIndicator( " " );
539 m_pDocument->visitedBy( *optimizingVisitor, std::list<std::unique_ptr<Element>>::const_iterator());
540
541#if OSL_DEBUG_LEVEL1 > 0
542 m_pDocument->emitStructure( 0 );
543#endif
544
545 // get styles
546 StyleContainer aStyles;
547 ElementTreeVisitorSharedPtr finalizingVisitor(
548 rVisitorFactory.createStyleCollectingVisitor(aStyles,*this));
549 // FIXME: localization
550
551 m_pDocument->visitedBy( *finalizingVisitor, std::list<std::unique_ptr<Element>>::const_iterator() );
552
553 EmitContext aContext( rEmitter, aStyles, m_aImages, *this, m_xStatusIndicator, m_xContext );
554 ElementTreeVisitorSharedPtr aEmittingVisitor(
555 rVisitorFactory.createEmittingVisitor(aContext));
556
557 PropertyMap aProps;
558 // document prolog
559 #define OASIS_STR"urn:oasis:names:tc:opendocument:xmlns:" "urn:oasis:names:tc:opendocument:xmlns:"
560 aProps[ "xmlns:office" ] = OASIS_STR"urn:oasis:names:tc:opendocument:xmlns:" "office:1.0" ;
561 aProps[ "xmlns:style" ] = OASIS_STR"urn:oasis:names:tc:opendocument:xmlns:" "style:1.0" ;
562 aProps[ "xmlns:text" ] = OASIS_STR"urn:oasis:names:tc:opendocument:xmlns:" "text:1.0" ;
563 aProps[ "xmlns:svg" ] = OASIS_STR"urn:oasis:names:tc:opendocument:xmlns:" "svg-compatible:1.0" ;
564 aProps[ "xmlns:table" ] = OASIS_STR"urn:oasis:names:tc:opendocument:xmlns:" "table:1.0" ;
565 aProps[ "xmlns:draw" ] = OASIS_STR"urn:oasis:names:tc:opendocument:xmlns:" "drawing:1.0" ;
566 aProps[ "xmlns:fo" ] = OASIS_STR"urn:oasis:names:tc:opendocument:xmlns:" "xsl-fo-compatible:1.0" ;
567 aProps[ "xmlns:xlink"] = "http://www.w3.org/1999/xlink";
568 aProps[ "xmlns:dc"] = "http://purl.org/dc/elements/1.1/";
569 aProps[ "xmlns:number"] = OASIS_STR"urn:oasis:names:tc:opendocument:xmlns:" "datastyle:1.0" ;
570 aProps[ "xmlns:presentation"] = OASIS_STR"urn:oasis:names:tc:opendocument:xmlns:" "presentation:1.0" ;
571 aProps[ "xmlns:math"] = "http://www.w3.org/1998/Math/MathML";
572 aProps[ "xmlns:form"] = OASIS_STR"urn:oasis:names:tc:opendocument:xmlns:" "form:1.0" ;
573 aProps[ "xmlns:script"] = OASIS_STR"urn:oasis:names:tc:opendocument:xmlns:" "script:1.0" ;
574 aProps[ "xmlns:dom"] = "http://www.w3.org/2001/xml-events";
575 aProps[ "xmlns:xforms"] = "http://www.w3.org/2002/xforms";
576 aProps[ "xmlns:xsd"] = "http://www.w3.org/2001/XMLSchema";
577 aProps[ "xmlns:xsi"] = "http://www.w3.org/2001/XMLSchema-instance";
578 aProps[ "office:version" ] = "1.0";
579
580 aContext.rEmitter.beginTag( "office:document", aProps );
581
582 // emit style list
583 aStyles.emit( aContext, *aEmittingVisitor );
584
585 m_pDocument->visitedBy( *aEmittingVisitor, std::list<std::unique_ptr<Element>>::const_iterator() );
586 aContext.rEmitter.endTag( "office:document" );
587 endIndicator();
588}
589
590void PDFIProcessor::startIndicator( const OUString& rText )
591{
592 sal_Int32 nElements = m_nPages;
593 if( !m_xStatusIndicator.is() )
594 return;
595
596 sal_Int32 nLength = rText.getLength();
597 OUStringBuffer aStr( nLength*2 );
598 const sal_Unicode* pText = rText.getStr();
599 for( int i = 0; i < nLength; i++ )
600 {
601 if( nLength-i > 1&&
602 pText[i] == '%' &&
603 pText[i+1] == 'd'
604 )
605 {
606 aStr.append( nElements );
607 i++;
608 }
609 else
610 aStr.append( pText[i] );
611 }
612 m_xStatusIndicator->start( aStr.makeStringAndClear(), nElements );
613}
614
615void PDFIProcessor::endIndicator()
616{
617 if( m_xStatusIndicator.is() )
618 m_xStatusIndicator->end();
619}
620
621static bool lr_tb_sort( std::unique_ptr<Element> const & pLeft, std::unique_ptr<Element> const & pRight )
622{
623 // Ensure irreflexivity (which could be compromised if h or w is negative):
624 if (pLeft == pRight)
625 return false;
626
627 // first: top-bottom sorting
628
629 // Note: allow for 10% overlap on text lines since text lines are usually
630 // of the same order as font height whereas the real paint area
631 // of text is usually smaller
632 double fudge_factor_left = 0.0, fudge_factor_right = 0.0;
633 if( dynamic_cast< TextElement* >(pLeft.get()) )
634 fudge_factor_left = 0.1;
635 if (dynamic_cast< TextElement* >(pRight.get()))
636 fudge_factor_right = 0.1;
637
638 // Allow negative height
639 double lower_boundary_left = pLeft->y + std::max(pLeft->h, 0.0) - fabs(pLeft->h) * fudge_factor_left;
640 double lower_boundary_right = pRight->y + std::max(pRight->h, 0.0) - fabs(pRight->h) * fudge_factor_right;
641 double upper_boundary_left = pLeft->y + std::min(pLeft->h, 0.0);
642 double upper_boundary_right = pRight->y + std::min(pRight->h, 0.0);
643 // if left's lower boundary is above right's upper boundary
644 // then left is smaller
645 if( lower_boundary_left < upper_boundary_right )
646 return true;
647 // if right's lower boundary is above left's upper boundary
648 // then left is definitely not smaller
649 if( lower_boundary_right < upper_boundary_left )
650 return false;
651
652 // Allow negative width
653 double left_boundary_left = pLeft->y + std::min(pLeft->w, 0.0);
654 double left_boundary_right = pRight->y + std::min(pRight->w, 0.0);
655 double right_boundary_left = pLeft->y + std::max(pLeft->w, 0.0);
656 double right_boundary_right = pRight->y + std::max(pRight->w, 0.0);
657 // by now we have established that left and right are inside
658 // a "line", that is they have vertical overlap
659 // second: left-right sorting
660 // if left's right boundary is left to right's left boundary
661 // then left is smaller
662 if( right_boundary_left < left_boundary_right )
663 return true;
664 // if right's right boundary is left to left's left boundary
665 // then left is definitely not smaller
666 if( right_boundary_right < left_boundary_left )
667 return false;
668
669 // here we have established vertical and horizontal overlap
670 // so sort left first, top second
671 if( pLeft->x < pRight->x )
672 return true;
673 if( pRight->x < pLeft->x )
674 return false;
675 if( pLeft->y < pRight->y )
676 return true;
677
678 return false;
679}
680
681void PDFIProcessor::sortElements(Element* pEle)
682{
683 if( pEle->Children.empty() )
684 return;
685
686 // sort method from std::list is equivalent to stable_sort
687 // See S Meyers, Effective STL
688 pEle->Children.sort(lr_tb_sort);
689}
690
691// helper method: get a mirrored string
692OUString PDFIProcessor::mirrorString( const OUString& i_rString )
693{
694 const sal_Int32 nLen = i_rString.getLength();
695 OUStringBuffer aMirror( nLen );
696
697 sal_Int32 i = 0;
698 while(i < nLen)
699 {
700 // read one code point
701 const sal_uInt32 nCodePoint = i_rString.iterateCodePoints( &i );
702
703 // and append it mirrored
704 aMirror.appendUtf32( GetMirroredChar(nCodePoint) );
705 }
706 return aMirror.makeStringAndClear();
707}
708
709}
710
711/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

/home/maarten/src/libreoffice/core/sdext/source/pdfimport/inc/genericelements.hxx

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#ifndef INCLUDED_SDEXT_SOURCE_PDFIMPORT_INC_GENERICELEMENTS_HXX
21#define INCLUDED_SDEXT_SOURCE_PDFIMPORT_INC_GENERICELEMENTS_HXX
22
23#include "pdfihelper.hxx"
24#include "treevisiting.hxx"
25
26#include <com/sun/star/task/XStatusIndicator.hpp>
27#include <com/sun/star/uno/XComponentContext.hpp>
28#include <basegfx/polygon/b2dpolypolygon.hxx>
29#include <rtl/ustring.hxx>
30#include <rtl/ustrbuf.hxx>
31
32#include <list>
33
34namespace pdfi
35{
36 class XmlEmitter;
37 class StyleContainer;
38 class ImageContainer;
39 class PDFIProcessor;
40 class ElementFactory;
41
42
43 struct EmitContext
44 {
45 EmitContext(
46 XmlEmitter& _rEmitter,
47 StyleContainer& _rStyles,
48 ImageContainer& _rImages,
49 PDFIProcessor& _rProcessor,
50 const css::uno::Reference<
51 css::task::XStatusIndicator>& _xStatusIndicator,
52 css::uno::Reference< css::uno::XComponentContext > const & xContext)
53 :
54 rEmitter(_rEmitter),
55 rStyles(_rStyles),
56 rImages(_rImages),
57 rProcessor(_rProcessor),
58 xStatusIndicator(_xStatusIndicator),
59 m_xContext(xContext)
60 {}
61
62 XmlEmitter& rEmitter;
63 StyleContainer& rStyles;
64 ImageContainer& rImages;
65 PDFIProcessor& rProcessor;
66 css::uno::Reference<
67 css::task::XStatusIndicator> xStatusIndicator;
68 css::uno::Reference<
69 css::uno::XComponentContext > m_xContext;
70 };
71
72 struct Element
73 {
74 protected:
75 explicit Element( Element* pParent )
76 : x( 0 ), y( 0 ), w( 0 ), h( 0 ), StyleId( -1 ), Parent( pParent )
77 {
78 if( pParent )
79 pParent->Children.emplace_back( this );
80 }
81
82 public:
83 virtual ~Element();
84
85 /**
86 To be implemented by every tree node that needs to be
87 visitable.
88 */
89 virtual void visitedBy( ElementTreeVisitor&, const std::list< std::unique_ptr<Element> >::const_iterator& rParentIt ) = 0;
90 /// Apply visitor to all children
91 void applyToChildren( ElementTreeVisitor& );
92 /// Union element geometry with given element
93 void updateGeometryWith( const Element* pMergeFrom );
94
95#if OSL_DEBUG_LEVEL1 > 0
96 // xxx refact TODO: move code to visitor
97 virtual void emitStructure( int nLevel );
98#endif
99 /** el must be a valid dereferenceable iterator of el->Parent->Children
100 pNewParent must not be NULL
101 */
102 static void setParent( std::list<std::unique_ptr<Element>>::iterator const & el, Element* pNewParent );
103
104 double x, y, w, h;
105 sal_Int32 StyleId;
106 Element* Parent;
107 std::list<std::unique_ptr<Element>> Children;
108 };
109
110 struct ListElement : public Element
111 {
112 ListElement() : Element( nullptr ) {}
113 virtual void visitedBy( ElementTreeVisitor&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override;
114 };
115
116 struct HyperlinkElement : public Element
117 {
118 friend class ElementFactory;
119 protected:
120 HyperlinkElement( Element* pParent, const OUString& rURI )
121 : Element( pParent ), URI( rURI ) {}
122 public:
123 virtual void visitedBy( ElementTreeVisitor&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override;
124
125 OUString URI;
126 };
127
128 struct GraphicalElement : public Element
129 {
130 protected:
131 GraphicalElement(Element* pParent, sal_Int32 nGCId)
132 : Element(pParent)
133 , GCId(nGCId)
134 , MirrorVertical(false)
135 , IsForText(false)
136 , FontSize(0.0)
137 , TextStyleId(0)
138 {
139 }
140
141 public:
142 sal_Int32 GCId;
143 bool MirrorVertical;
144 bool IsForText;
145 double FontSize;
146 sal_Int32 TextStyleId;
147 };
148
149 struct DrawElement : public GraphicalElement
150 {
151 protected:
152 DrawElement( Element* pParent, sal_Int32 nGCId )
153 : GraphicalElement( pParent, nGCId ), isCharacter(false), ZOrder(0) {}
154
155 public:
156 bool isCharacter;
157 sal_Int32 ZOrder;
158 };
159
160 struct FrameElement : public DrawElement
161 {
162 friend class ElementFactory;
163 protected:
164 FrameElement( Element* pParent, sal_Int32 nGCId )
165 : DrawElement( pParent, nGCId ) {}
166
167 public:
168 virtual void visitedBy( ElementTreeVisitor&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override;
169 };
170
171 struct TextElement : public GraphicalElement
172 {
173 friend class ElementFactory;
174 protected:
175 TextElement( Element* pParent, sal_Int32 nGCId, sal_Int32 nFontId )
176 : GraphicalElement( pParent, nGCId ), FontId( nFontId ) {}
177
178 public:
179 virtual void visitedBy( ElementTreeVisitor&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override;
180
181 OUStringBuffer Text;
182 sal_Int32 FontId;
183 };
184
185 struct ParagraphElement : public Element
186 {
187 friend class ElementFactory;
188 protected:
189 explicit ParagraphElement( Element* pParent ) : Element( pParent ), Type( Normal ), bRtl( false ) {}
190
191 public:
192 virtual void visitedBy( ElementTreeVisitor&, const std::list< std::unique_ptr<Element> >::const_iterator& rParentIt ) override;
193
194 // returns true only if only a single line is contained
195 bool isSingleLined( PDFIProcessor const & rProc ) const;
196 // returns the highest line height of the contained textelements
197 // line height is font height if the text element is itself multilined
198 double getLineHeight( PDFIProcessor& rProc ) const;
199 // returns the first text element child; does not recurse through subparagraphs
200 TextElement* getFirstTextChild() const;
201
202 enum ParagraphType { Normal, Headline };
203 ParagraphType Type;
204 bool bRtl;
205 };
206
207 struct PolyPolyElement : public DrawElement
208 {
209 friend class ElementFactory;
210 protected:
211 PolyPolyElement( Element* pParent, sal_Int32 nGCId,
212 const basegfx::B2DPolyPolygon& rPolyPoly,
213 sal_Int8 nAction );
214 public:
215 virtual void visitedBy( ElementTreeVisitor&, const std::list< std::unique_ptr<Element> >::const_iterator& rParentIt ) override;
216
217 void updateGeometry();
218
219#if OSL_DEBUG_LEVEL1 > 0
220 virtual void emitStructure( int nLevel ) override;
221#endif
222
223 basegfx::B2DPolyPolygon PolyPoly;
224 sal_Int8 Action;
225 };
226
227 struct ImageElement : public DrawElement
228 {
229 friend class ElementFactory;
230 protected:
231 ImageElement( Element* pParent, sal_Int32 nGCId, ImageId nImage )
232 : DrawElement( pParent, nGCId ), Image( nImage ) {}
233
234 public:
235 virtual void visitedBy( ElementTreeVisitor&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override;
236
237 ImageId Image;
238 };
239
240 struct PageElement : public Element
241 {
242 friend class ElementFactory;
243 protected:
244 PageElement( Element* pParent, sal_Int32 nPageNr )
245 : Element( pParent ), PageNumber( nPageNr ), Hyperlinks(),
246 TopMargin( 0.0 ), BottomMargin( 0.0 ), LeftMargin( 0.0 ), RightMargin( 0.0 )
247 {}
248 private:
249 // helper method for resolveHyperlinks
250 bool resolveHyperlink( const std::list<std::unique_ptr<Element>>::iterator& link_it, std::list<std::unique_ptr<Element>>& rElements );
251 public:
252 virtual ~PageElement() override;
253
254 virtual void visitedBy( ElementTreeVisitor&, const std::list< std::unique_ptr<Element> >::const_iterator& rParentIt ) override;
255
256 void resolveHyperlinks();
257 void resolveFontStyles( PDFIProcessor const & rProc );
258 void resolveUnderlines( PDFIProcessor const & rProc );
259
260 sal_Int32 PageNumber;
261 ListElement Hyperlinks; // contains not yet realized links on this page
262 double TopMargin;
263 double BottomMargin;
264 double LeftMargin;
265 double RightMargin;
266 std::unique_ptr<Element> HeaderElement;
267 std::unique_ptr<Element> FooterElement;
268 };
269
270 struct DocumentElement : public Element
271 {
272 friend class ElementFactory;
273 public:
274 DocumentElement() : Element( nullptr ) {}
275 virtual ~DocumentElement() override;
276
277 virtual void visitedBy( ElementTreeVisitor&, const std::list< std::unique_ptr<Element> >::const_iterator& ) override;
278 };
279
280 // this class is the differentiator of document types: it will create
281 // Element objects with an optimize() method suitable for the document type
282 class ElementFactory
283 {
284 public:
285 ElementFactory() = delete;
286
287 static HyperlinkElement* createHyperlinkElement( Element* pParent, const OUString& rURI )
288 { return new HyperlinkElement( pParent, rURI ); }
3
Memory is allocated
289
290 static TextElement* createTextElement( Element* pParent, sal_Int32 nGCId, sal_Int32 nFontId )
291 { return new TextElement( pParent, nGCId, nFontId ); }
292 static ParagraphElement* createParagraphElement( Element* pParent )
293 { return new ParagraphElement( pParent ); }
294
295 static FrameElement* createFrameElement( Element* pParent, sal_Int32 nGCId )
296 { return new FrameElement( pParent, nGCId ); }
297 static PolyPolyElement*
298 createPolyPolyElement( Element* pParent,
299 sal_Int32 nGCId,
300 const basegfx::B2DPolyPolygon& rPolyPoly,
301 sal_Int8 nAction)
302 { return new PolyPolyElement( pParent, nGCId, rPolyPoly, nAction ); }
303 static ImageElement* createImageElement( Element* pParent, sal_Int32 nGCId, ImageId nImage )
304 { return new ImageElement( pParent, nGCId, nImage ); }
305
306 static PageElement* createPageElement( Element* pParent,
307 sal_Int32 nPageNr )
308 { return new PageElement( pParent, nPageNr ); }
309 static std::shared_ptr<DocumentElement> createDocumentElement()
310 { return std::make_shared<DocumentElement>(); }
311 };
312}
313
314#endif
315
316/* vim:set shiftwidth=4 softtabstop=4 expandtab: */