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_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_RESOLUTION/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 
78 void PDFIProcessor::setPageNum( sal_Int32 nPages )
79 {
80  m_nPages = nPages;
81 }
82 
83 
85 {
86  GraphicsContextStack::value_type const a(m_aGCStack.back());
87  m_aGCStack.push_back(a);
88 }
89 
91 {
92  m_aGCStack.pop_back();
93 }
94 
95 void PDFIProcessor::setFlatness( double value )
96 {
97  getCurrentContext().Flatness = value;
98 }
99 
100 void PDFIProcessor::setTransformation( const geometry::AffineMatrix2D& rMatrix )
101 {
104  rMatrix );
105 }
106 
107 void PDFIProcessor::setLineDash( const uno::Sequence<double>& dashes,
108  double /*start*/ )
109 {
110  // TODO(F2): factor in start offset
111  GraphicsContext& rContext( getCurrentContext() );
113 }
114 
116 {
117  getCurrentContext().LineJoin = nJoin;
118 }
119 
121 {
122  getCurrentContext().LineCap = nCap;
123 }
124 
126 {
127  SAL_WARN("sdext.pdfimport", "PDFIProcessor::setMiterLimit(): not supported by ODF");
128 }
129 
130 void PDFIProcessor::setLineWidth(double nWidth)
131 {
132  getCurrentContext().LineWidth = nWidth;
133 }
134 
135 void PDFIProcessor::setFillColor( const rendering::ARGBColor& rColor )
136 {
137  getCurrentContext().FillColor = rColor;
138 }
139 
140 void PDFIProcessor::setStrokeColor( const rendering::ARGBColor& rColor )
141 {
142  getCurrentContext().LineColor = rColor;
143 }
144 
146 {
147  FontAttributes aChangedFont( i_rFont );
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 
163 void PDFIProcessor::setTextRenderMode( sal_Int32 i_nMode )
164 {
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 
172 sal_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
183 void 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 
254 void 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 
311 void PDFIProcessor::endText()
312 {
313  TextElement* pText = dynamic_cast<TextElement*>(m_pCurElement);
314  if( pText )
315  m_pCurElement = pText->Parent;
316 }
317 
318 void 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 
343 void 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 
350 void PDFIProcessor::drawImage(const uno::Sequence<beans::PropertyValue>& xBitmap )
351 {
352  setupImage( m_aImages.addImage(xBitmap) );
353 }
354 
355 void 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 
362 void 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 
370 void 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 
379 void PDFIProcessor::strokePath( const uno::Reference< rendering::XPolyPolygon2D >& rPath )
380 {
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 
393 void PDFIProcessor::fillPath( const uno::Reference< rendering::XPolyPolygon2D >& rPath )
394 {
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 
407 void PDFIProcessor::eoFillPath( const uno::Reference< rendering::XPolyPolygon2D >& rPath )
408 {
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 
421 void PDFIProcessor::intersectClip(const uno::Reference< rendering::XPolyPolygon2D >& rPath)
422 {
423  // TODO(F3): interpret fill mode
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 
434 void PDFIProcessor::intersectEoClip(const uno::Reference< rendering::XPolyPolygon2D >& rPath)
435 {
436  // TODO(F3): interpret fill mode
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 
447 void PDFIProcessor::hyperLink( const geometry::RealRectangle2D& rBounds,
448  const OUString& rURI )
449 {
450  if( !rURI.isEmpty() )
451  {
452  HyperlinkElement* pLink = ElementFactory::createHyperlinkElement(
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 }
461 
462 const 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 
470 sal_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 
487 const 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 
495 void 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 
505 void PDFIProcessor::startPage( const geometry::RealSize2D& rSize )
506 {
507  // initial clip is to page bounds
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 
528 void PDFIProcessor::emit( XmlEmitter& rEmitter,
529  const TreeVisitorFactory& rVisitorFactory )
530 {
531 #if OSL_DEBUG_LEVEL > 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_LEVEL > 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:"
560  aProps[ "xmlns:office" ] = OASIS_STR "office:1.0" ;
561  aProps[ "xmlns:style" ] = OASIS_STR "style:1.0" ;
562  aProps[ "xmlns:text" ] = OASIS_STR "text:1.0" ;
563  aProps[ "xmlns:svg" ] = OASIS_STR "svg-compatible:1.0" ;
564  aProps[ "xmlns:table" ] = OASIS_STR "table:1.0" ;
565  aProps[ "xmlns:draw" ] = OASIS_STR "drawing:1.0" ;
566  aProps[ "xmlns:fo" ] = OASIS_STR "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 "datastyle:1.0" ;
570  aProps[ "xmlns:presentation"] = OASIS_STR "presentation:1.0" ;
571  aProps[ "xmlns:math"] = "http://www.w3.org/1998/Math/MathML";
572  aProps[ "xmlns:form"] = OASIS_STR "form:1.0" ;
573  aProps[ "xmlns:script"] = OASIS_STR "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 
590 void 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 
615 void PDFIProcessor::endIndicator()
616 {
617  if( m_xStatusIndicator.is() )
618  m_xStatusIndicator->end();
619 }
620 
621 static 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 
681 void 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
692 OUString 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: */
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
int i
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.
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)
sal_Int32 nLength
virtual void setFont(const FontAttributes &rFont) override
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