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