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