1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_sdext.hxx" 26 27 #include "xmlemitter.hxx" 28 #include "genericelements.hxx" 29 #include "pdfiprocessor.hxx" 30 #include "pdfihelper.hxx" 31 #include "style.hxx" 32 33 34 #include <basegfx/polygon/b2dpolypolygontools.hxx> 35 #include <basegfx/range/b2drange.hxx> 36 37 namespace pdfi 38 { 39 40 ElementFactory::~ElementFactory() 41 { 42 } 43 44 Element::~Element() 45 { 46 while( !Children.empty() ) 47 { 48 Element* pCurr( Children.front() ); 49 delete pCurr; 50 Children.pop_front(); 51 } 52 } 53 54 void Element::applyToChildren( ElementTreeVisitor& rVisitor ) 55 { 56 for( std::list< Element* >::iterator it = Children.begin(); it != Children.end(); ++it ) 57 (*it)->visitedBy( rVisitor, it ); 58 } 59 60 void Element::setParent( std::list<Element*>::iterator& el, Element* pNewParent ) 61 { 62 if( pNewParent ) 63 { 64 pNewParent->Children.splice( pNewParent->Children.end(), (*el)->Parent->Children, el ); 65 (*el)->Parent = pNewParent; 66 } 67 } 68 69 void Element::updateGeometryWith( const Element* pMergeFrom ) 70 { 71 if( w == 0 && h == 0 ) 72 { 73 x = pMergeFrom->x; 74 y = pMergeFrom->y; 75 w = pMergeFrom->w; 76 h = pMergeFrom->h; 77 } 78 else 79 { 80 if( pMergeFrom->x < x ) 81 { 82 w += x - pMergeFrom->x; 83 x = pMergeFrom->x; 84 } 85 if( pMergeFrom->x+pMergeFrom->w > x+w ) 86 w = pMergeFrom->w+pMergeFrom->x - x; 87 if( pMergeFrom->y < y ) 88 { 89 h += y - pMergeFrom->y; 90 y = pMergeFrom->y; 91 } 92 if( pMergeFrom->y+pMergeFrom->h > y+h ) 93 h = pMergeFrom->h+pMergeFrom->y - y; 94 } 95 } 96 97 98 #if OSL_DEBUG_LEVEL > 1 99 #include <typeinfo> 100 void Element::emitStructure( int nLevel) 101 { 102 OSL_TRACE( "%*s<%s %p> (%.1f,%.1f)+(%.1fx%.1f)\n", 103 nLevel, "", typeid( *this ).name(), this, 104 x, y, w, h ); 105 for( std::list< Element* >::iterator it = Children.begin(); it != Children.end(); ++it ) 106 (*it)->emitStructure(nLevel+1 ); 107 OSL_TRACE( "%*s</%s>\n", nLevel, "", typeid( *this ).name() ); 108 } 109 #endif 110 111 void ListElement::visitedBy( ElementTreeVisitor& visitor, const std::list< Element* >::const_iterator& ) 112 { 113 // this is only an inner node 114 applyToChildren(visitor); 115 } 116 117 void HyperlinkElement::visitedBy( ElementTreeVisitor& rVisitor, 118 const std::list< Element* >::const_iterator& rParentIt ) 119 { 120 rVisitor.visit(*this,rParentIt); 121 } 122 123 void TextElement::visitedBy( ElementTreeVisitor& rVisitor, 124 const std::list< Element* >::const_iterator& rParentIt ) 125 { 126 rVisitor.visit(*this,rParentIt); 127 } 128 129 void FrameElement::visitedBy( ElementTreeVisitor& rVisitor, 130 const std::list< Element* >::const_iterator& rParentIt ) 131 { 132 rVisitor.visit(*this,rParentIt); 133 } 134 135 void ImageElement::visitedBy( ElementTreeVisitor& rVisitor, 136 const std::list< Element* >::const_iterator& rParentIt) 137 { 138 rVisitor.visit( *this, rParentIt); 139 } 140 141 PolyPolyElement::PolyPolyElement( Element* pParent, 142 sal_Int32 nGCId, 143 const basegfx::B2DPolyPolygon& rPolyPoly, 144 sal_Int8 nAction ) 145 : DrawElement( pParent, nGCId ), 146 PolyPoly( rPolyPoly ), 147 Action( nAction ) 148 { 149 } 150 151 void PolyPolyElement::updateGeometry() 152 { 153 basegfx::B2DRange aRange; 154 if( PolyPoly.areControlPointsUsed() ) 155 aRange = basegfx::tools::getRange( basegfx::tools::adaptiveSubdivideByAngle( PolyPoly ) ); 156 else 157 aRange = basegfx::tools::getRange( PolyPoly ); 158 x = aRange.getMinX(); 159 y = aRange.getMinY(); 160 w = aRange.getWidth(); 161 h = aRange.getHeight(); 162 } 163 164 void PolyPolyElement::visitedBy( ElementTreeVisitor& rVisitor, 165 const std::list< Element* >::const_iterator& rParentIt) 166 { 167 rVisitor.visit( *this, rParentIt); 168 } 169 170 #if OSL_DEBUG_LEVEL > 1 171 void PolyPolyElement::emitStructure( int nLevel) 172 { 173 OSL_TRACE( "%*s<%s %p>\n", nLevel, "", typeid( *this ).name(), this ); 174 OSL_TRACE( "path=" ); 175 int nPoly = PolyPoly.count(); 176 for( int i = 0; i < nPoly; i++ ) 177 { 178 basegfx::B2DPolygon aPoly = PolyPoly.getB2DPolygon( i ); 179 int nPoints = aPoly.count(); 180 for( int n = 0; n < nPoints; n++ ) 181 { 182 basegfx::B2DPoint aPoint = aPoly.getB2DPoint( n ); 183 OSL_TRACE( " (%g,%g)", aPoint.getX(), aPoint.getY() ); 184 } 185 OSL_TRACE( "\n" ); 186 } 187 for( std::list< Element* >::iterator it = Children.begin(); it != Children.end(); ++it ) 188 (*it)->emitStructure( nLevel+1 ); 189 OSL_TRACE( "%*s</%s>\n", nLevel, "", typeid( *this ).name() ); 190 } 191 #endif 192 193 void ParagraphElement::visitedBy( ElementTreeVisitor& rVisitor, 194 const std::list< Element* >::const_iterator& rParentIt ) 195 { 196 rVisitor.visit(*this,rParentIt); 197 } 198 199 bool ParagraphElement::isSingleLined( PDFIProcessor& rProc ) const 200 { 201 std::list< Element* >::const_iterator it = Children.begin(); 202 TextElement* pText = NULL, *pLastText = NULL; 203 while( it != Children.end() ) 204 { 205 // a paragraph containing subparagraphs cannot be single lined 206 if( dynamic_cast< ParagraphElement* >(*it) != NULL ) 207 return false; 208 209 pText = dynamic_cast< TextElement* >(*it); 210 if( pText ) 211 { 212 const FontAttributes& rFont = rProc.getFont( pText->FontId ); 213 if( pText->h > rFont.size*1.5 ) 214 return false; 215 if( pLastText ) 216 { 217 if( pText->y > pLastText->y+pLastText->h || 218 pLastText->y > pText->y+pText->h ) 219 return false; 220 } 221 else 222 pLastText = pText; 223 } 224 ++it; 225 } 226 227 // a paragraph without a single text is not considered single lined 228 return pLastText != NULL; 229 } 230 231 double ParagraphElement::getLineHeight( PDFIProcessor& rProc ) const 232 { 233 double line_h = 0; 234 for( std::list< Element* >::const_iterator it = Children.begin(); it != Children.end(); ++it ) 235 { 236 ParagraphElement* pPara = dynamic_cast< ParagraphElement* >(*it); 237 TextElement* pText = NULL; 238 if( pPara ) 239 { 240 double lh = pPara->getLineHeight( rProc ); 241 if( lh > line_h ) 242 line_h = lh; 243 } 244 else if( (pText = dynamic_cast< TextElement* >( *it )) != NULL ) 245 { 246 const FontAttributes& rFont = rProc.getFont( pText->FontId ); 247 double lh = pText->h; 248 if( pText->h > rFont.size*1.5 ) 249 lh = rFont.size; 250 if( lh > line_h ) 251 line_h = lh; 252 } 253 } 254 return line_h; 255 } 256 257 TextElement* ParagraphElement::getFirstTextChild() const 258 { 259 TextElement* pText = NULL; 260 for( std::list< Element* >::const_iterator it = Children.begin(); 261 it != Children.end() && ! pText; ++it ) 262 { 263 pText = dynamic_cast<TextElement*>(*it); 264 } 265 return pText; 266 } 267 268 PageElement::~PageElement() 269 { 270 if( HeaderElement ) 271 delete HeaderElement; 272 if( FooterElement ) 273 delete FooterElement; 274 } 275 276 void PageElement::visitedBy( ElementTreeVisitor& rVisitor, 277 const std::list< Element* >::const_iterator& rParentIt ) 278 { 279 rVisitor.visit(*this, rParentIt); 280 } 281 282 void PageElement::updateParagraphGeometry( Element* pEle ) 283 { 284 // update geometry of children 285 for( std::list< Element* >::iterator it = pEle->Children.begin(); 286 it != pEle->Children.end(); ++it ) 287 { 288 updateParagraphGeometry( *it ); 289 } 290 // if this is a paragraph itself, then update according to children geometry 291 if( dynamic_cast<ParagraphElement*>(pEle) ) 292 { 293 for( std::list< Element* >::iterator it = pEle->Children.begin(); 294 it != pEle->Children.end(); ++it ) 295 { 296 Element* pChild = NULL; 297 TextElement* pText = dynamic_cast<TextElement*>(*it); 298 if( pText ) 299 pChild = pText; 300 else 301 { 302 ParagraphElement* pPara = dynamic_cast<ParagraphElement*>(*it); 303 if( pPara ) 304 pChild = pPara; 305 } 306 if( pChild ) 307 pEle->updateGeometryWith( pChild ); 308 } 309 } 310 } 311 312 bool PageElement::resolveHyperlink( std::list<Element*>::iterator link_it, std::list<Element*>& rElements ) 313 { 314 HyperlinkElement* pLink = dynamic_cast<HyperlinkElement*>(*link_it); 315 if( ! pLink ) // sanity check 316 return false; 317 318 for( std::list<Element*>::iterator it = rElements.begin(); it != rElements.end(); ++it ) 319 { 320 if( (*it)->x >= pLink->x && (*it)->x + (*it)->w <= pLink->x + pLink->w && 321 (*it)->y >= pLink->y && (*it)->y + (*it)->h <= pLink->y + pLink->h ) 322 { 323 TextElement* pText = dynamic_cast<TextElement*>(*it); 324 if( pText ) 325 { 326 if( pLink->Children.empty() ) 327 { 328 // insert the hyperlink before the frame 329 rElements.splice( it, Hyperlinks.Children, link_it ); 330 pLink->Parent = (*it)->Parent; 331 } 332 // move text element into hyperlink 333 std::list<Element*>::iterator next = it; 334 ++next; 335 Element::setParent( it, pLink ); 336 it = next; 337 --it; 338 continue; 339 } 340 // a link can contain multiple text elements or a single frame 341 if( ! pLink->Children.empty() ) 342 continue; 343 if( dynamic_cast<ParagraphElement*>(*it) ) 344 { 345 if( resolveHyperlink( link_it, (*it)->Children ) ) 346 break; 347 continue; 348 } 349 FrameElement* pFrame = dynamic_cast<FrameElement*>(*it); 350 if( pFrame ) 351 { 352 // insert the hyperlink before the frame 353 rElements.splice( it, Hyperlinks.Children, link_it ); 354 pLink->Parent = (*it)->Parent; 355 // move frame into hyperlink 356 Element::setParent( it, pLink ); 357 break; 358 } 359 } 360 } 361 return ! pLink->Children.empty(); 362 } 363 364 void PageElement::resolveHyperlinks() 365 { 366 while( ! Hyperlinks.Children.empty() ) 367 { 368 if( ! resolveHyperlink( Hyperlinks.Children.begin(), Children ) ) 369 { 370 delete Hyperlinks.Children.front(); 371 Hyperlinks.Children.pop_front(); 372 } 373 } 374 } 375 376 void PageElement::resolveFontStyles( PDFIProcessor& rProc ) 377 { 378 resolveUnderlines(rProc); 379 } 380 381 void PageElement::resolveUnderlines( PDFIProcessor& rProc ) 382 { 383 // FIXME: currently the algorithm used is quadratic 384 // this could be solved by some sorting beforehand 385 386 std::list< Element* >::iterator poly_it = Children.begin(); 387 while( poly_it != Children.end() ) 388 { 389 PolyPolyElement* pPoly = dynamic_cast< PolyPolyElement* >(*poly_it); 390 if( ! pPoly || ! pPoly->Children.empty() ) 391 { 392 ++poly_it; 393 continue; 394 } 395 /* check for: no filling 396 * only two points (FIXME: handle small rectangles, too) 397 * y coordinates of points are equal 398 */ 399 if( pPoly->Action != PATH_STROKE ) 400 { 401 ++poly_it; 402 continue; 403 } 404 if( pPoly->PolyPoly.count() != 1 ) 405 { 406 ++poly_it; 407 continue; 408 } 409 410 bool bRemovePoly = false; 411 basegfx::B2DPolygon aPoly = pPoly->PolyPoly.getB2DPolygon(0); 412 if( aPoly.count() != 2 || 413 aPoly.getB2DPoint(0).getY() != aPoly.getB2DPoint(1).getY() ) 414 { 415 ++poly_it; 416 continue; 417 } 418 double l_x = aPoly.getB2DPoint(0).getX(); 419 double r_x = aPoly.getB2DPoint(1).getX(); 420 double u_y; 421 if( r_x < l_x ) 422 { 423 u_y = r_x; r_x = l_x; l_x = u_y; 424 } 425 u_y = aPoly.getB2DPoint(0).getY(); 426 for( std::list< Element*>::iterator it = Children.begin(); 427 it != Children.end(); ++it ) 428 { 429 Element* pEle = *it; 430 if( pEle->y <= u_y && pEle->y + pEle->h*1.1 >= u_y ) 431 { 432 // first: is the element underlined completely ? 433 if( pEle->x + pEle->w*0.1 >= l_x && 434 pEle->x + pEle->w*0.9 <= r_x ) 435 { 436 TextElement* pText = dynamic_cast< TextElement* >(pEle); 437 if( pText ) 438 { 439 const GraphicsContext& rTextGC = rProc.getGraphicsContext( pText->GCId ); 440 if( ! rTextGC.isRotatedOrSkewed() ) 441 { 442 bRemovePoly = true; 443 // retrieve ID for modified font 444 FontAttributes aAttr = rProc.getFont( pText->FontId ); 445 aAttr.isUnderline = true; 446 pText->FontId = rProc.getFontId( aAttr ); 447 } 448 } 449 else if( dynamic_cast< HyperlinkElement* >(pEle) ) 450 bRemovePoly = true; 451 } 452 // second: hyperlinks may be larger than their underline 453 // since they are just arbitrary rectangles in the action definition 454 else if( dynamic_cast< HyperlinkElement* >(pEle) != NULL && 455 l_x >= pEle->x && r_x <= pEle->x+pEle->w ) 456 { 457 bRemovePoly = true; 458 } 459 } 460 } 461 if( bRemovePoly ) 462 { 463 std::list< Element* >::iterator next_it = poly_it; 464 ++next_it; 465 Children.erase( poly_it ); 466 delete pPoly; 467 poly_it = next_it; 468 } 469 else 470 ++poly_it; 471 } 472 } 473 474 DocumentElement::~DocumentElement() 475 { 476 } 477 478 void DocumentElement::visitedBy( ElementTreeVisitor& rVisitor, 479 const std::list< Element* >::const_iterator& rParentIt) 480 { 481 rVisitor.visit(*this, rParentIt); 482 } 483 484 485 } 486