/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_sdext.hxx" #include "xmlemitter.hxx" #include "genericelements.hxx" #include "pdfiprocessor.hxx" #include "pdfihelper.hxx" #include "style.hxx" #include #include namespace pdfi { ElementFactory::~ElementFactory() { } Element::~Element() { while( !Children.empty() ) { Element* pCurr( Children.front() ); delete pCurr; Children.pop_front(); } } void Element::applyToChildren( ElementTreeVisitor& rVisitor ) { for( std::list< Element* >::iterator it = Children.begin(); it != Children.end(); ++it ) (*it)->visitedBy( rVisitor, it ); } void Element::setParent( std::list::iterator& el, Element* pNewParent ) { if( pNewParent ) { pNewParent->Children.splice( pNewParent->Children.end(), (*el)->Parent->Children, el ); (*el)->Parent = pNewParent; } } void Element::updateGeometryWith( const Element* pMergeFrom ) { if( w == 0 && h == 0 ) { x = pMergeFrom->x; y = pMergeFrom->y; w = pMergeFrom->w; h = pMergeFrom->h; } else { if( pMergeFrom->x < x ) { w += x - pMergeFrom->x; x = pMergeFrom->x; } if( pMergeFrom->x+pMergeFrom->w > x+w ) w = pMergeFrom->w+pMergeFrom->x - x; if( pMergeFrom->y < y ) { h += y - pMergeFrom->y; y = pMergeFrom->y; } if( pMergeFrom->y+pMergeFrom->h > y+h ) h = pMergeFrom->h+pMergeFrom->y - y; } } #if OSL_DEBUG_LEVEL > 1 #include void Element::emitStructure( int nLevel) { OSL_TRACE( "%*s<%s %p> (%.1f,%.1f)+(%.1fx%.1f)\n", nLevel, "", typeid( *this ).name(), this, x, y, w, h ); for( std::list< Element* >::iterator it = Children.begin(); it != Children.end(); ++it ) (*it)->emitStructure(nLevel+1 ); OSL_TRACE( "%*s\n", nLevel, "", typeid( *this ).name() ); } #endif void ListElement::visitedBy( ElementTreeVisitor& visitor, const std::list< Element* >::const_iterator& ) { // this is only an inner node applyToChildren(visitor); } void HyperlinkElement::visitedBy( ElementTreeVisitor& rVisitor, const std::list< Element* >::const_iterator& rParentIt ) { rVisitor.visit(*this,rParentIt); } void TextElement::visitedBy( ElementTreeVisitor& rVisitor, const std::list< Element* >::const_iterator& rParentIt ) { rVisitor.visit(*this,rParentIt); } void FrameElement::visitedBy( ElementTreeVisitor& rVisitor, const std::list< Element* >::const_iterator& rParentIt ) { rVisitor.visit(*this,rParentIt); } void ImageElement::visitedBy( ElementTreeVisitor& rVisitor, const std::list< Element* >::const_iterator& rParentIt) { rVisitor.visit( *this, rParentIt); } PolyPolyElement::PolyPolyElement( Element* pParent, sal_Int32 nGCId, const basegfx::B2DPolyPolygon& rPolyPoly, sal_Int8 nAction ) : DrawElement( pParent, nGCId ), PolyPoly( rPolyPoly ), Action( nAction ) { } void PolyPolyElement::updateGeometry() { basegfx::B2DRange aRange; if( PolyPoly.areControlPointsUsed() ) aRange = basegfx::tools::getRange( basegfx::tools::adaptiveSubdivideByAngle( PolyPoly ) ); else aRange = basegfx::tools::getRange( PolyPoly ); x = aRange.getMinX(); y = aRange.getMinY(); w = aRange.getWidth(); h = aRange.getHeight(); } void PolyPolyElement::visitedBy( ElementTreeVisitor& rVisitor, const std::list< Element* >::const_iterator& rParentIt) { rVisitor.visit( *this, rParentIt); } #if OSL_DEBUG_LEVEL > 1 void PolyPolyElement::emitStructure( int nLevel) { OSL_TRACE( "%*s<%s %p>\n", nLevel, "", typeid( *this ).name(), this ); OSL_TRACE( "path=" ); int nPoly = PolyPoly.count(); for( int i = 0; i < nPoly; i++ ) { basegfx::B2DPolygon aPoly = PolyPoly.getB2DPolygon( i ); int nPoints = aPoly.count(); for( int n = 0; n < nPoints; n++ ) { basegfx::B2DPoint aPoint = aPoly.getB2DPoint( n ); OSL_TRACE( " (%g,%g)", aPoint.getX(), aPoint.getY() ); } OSL_TRACE( "\n" ); } for( std::list< Element* >::iterator it = Children.begin(); it != Children.end(); ++it ) (*it)->emitStructure( nLevel+1 ); OSL_TRACE( "%*s\n", nLevel, "", typeid( *this ).name() ); } #endif void ParagraphElement::visitedBy( ElementTreeVisitor& rVisitor, const std::list< Element* >::const_iterator& rParentIt ) { rVisitor.visit(*this,rParentIt); } bool ParagraphElement::isSingleLined( PDFIProcessor& rProc ) const { std::list< Element* >::const_iterator it = Children.begin(); TextElement* pText = NULL, *pLastText = NULL; while( it != Children.end() ) { // a paragraph containing subparagraphs cannot be single lined if( dynamic_cast< ParagraphElement* >(*it) != NULL ) return false; pText = dynamic_cast< TextElement* >(*it); if( pText ) { const FontAttributes& rFont = rProc.getFont( pText->FontId ); if( pText->h > rFont.size*1.5 ) return false; if( pLastText ) { if( pText->y > pLastText->y+pLastText->h || pLastText->y > pText->y+pText->h ) return false; } else pLastText = pText; } ++it; } // a paragraph without a single text is not considered single lined return pLastText != NULL; } double ParagraphElement::getLineHeight( PDFIProcessor& rProc ) const { double line_h = 0; for( std::list< Element* >::const_iterator it = Children.begin(); it != Children.end(); ++it ) { ParagraphElement* pPara = dynamic_cast< ParagraphElement* >(*it); TextElement* pText = NULL; if( pPara ) { double lh = pPara->getLineHeight( rProc ); if( lh > line_h ) line_h = lh; } else if( (pText = dynamic_cast< TextElement* >( *it )) != NULL ) { const FontAttributes& rFont = rProc.getFont( pText->FontId ); double lh = pText->h; if( pText->h > rFont.size*1.5 ) lh = rFont.size; if( lh > line_h ) line_h = lh; } } return line_h; } TextElement* ParagraphElement::getFirstTextChild() const { TextElement* pText = NULL; for( std::list< Element* >::const_iterator it = Children.begin(); it != Children.end() && ! pText; ++it ) { pText = dynamic_cast(*it); } return pText; } PageElement::~PageElement() { if( HeaderElement ) delete HeaderElement; if( FooterElement ) delete FooterElement; } void PageElement::visitedBy( ElementTreeVisitor& rVisitor, const std::list< Element* >::const_iterator& rParentIt ) { rVisitor.visit(*this, rParentIt); } void PageElement::updateParagraphGeometry( Element* pEle ) { // update geometry of children for( std::list< Element* >::iterator it = pEle->Children.begin(); it != pEle->Children.end(); ++it ) { updateParagraphGeometry( *it ); } // if this is a paragraph itself, then update according to children geometry if( dynamic_cast(pEle) ) { for( std::list< Element* >::iterator it = pEle->Children.begin(); it != pEle->Children.end(); ++it ) { Element* pChild = NULL; TextElement* pText = dynamic_cast(*it); if( pText ) pChild = pText; else { ParagraphElement* pPara = dynamic_cast(*it); if( pPara ) pChild = pPara; } if( pChild ) pEle->updateGeometryWith( pChild ); } } } bool PageElement::resolveHyperlink( std::list::iterator link_it, std::list& rElements ) { HyperlinkElement* pLink = dynamic_cast(*link_it); if( ! pLink ) // sanity check return false; for( std::list::iterator it = rElements.begin(); it != rElements.end(); ++it ) { if( (*it)->x >= pLink->x && (*it)->x + (*it)->w <= pLink->x + pLink->w && (*it)->y >= pLink->y && (*it)->y + (*it)->h <= pLink->y + pLink->h ) { TextElement* pText = dynamic_cast(*it); if( pText ) { if( pLink->Children.empty() ) { // insert the hyperlink before the frame rElements.splice( it, Hyperlinks.Children, link_it ); pLink->Parent = (*it)->Parent; } // move text element into hyperlink std::list::iterator next = it; ++next; Element::setParent( it, pLink ); it = next; --it; continue; } // a link can contain multiple text elements or a single frame if( ! pLink->Children.empty() ) continue; if( dynamic_cast(*it) ) { if( resolveHyperlink( link_it, (*it)->Children ) ) break; continue; } FrameElement* pFrame = dynamic_cast(*it); if( pFrame ) { // insert the hyperlink before the frame rElements.splice( it, Hyperlinks.Children, link_it ); pLink->Parent = (*it)->Parent; // move frame into hyperlink Element::setParent( it, pLink ); break; } } } return ! pLink->Children.empty(); } void PageElement::resolveHyperlinks() { while( ! Hyperlinks.Children.empty() ) { if( ! resolveHyperlink( Hyperlinks.Children.begin(), Children ) ) { delete Hyperlinks.Children.front(); Hyperlinks.Children.pop_front(); } } } void PageElement::resolveFontStyles( PDFIProcessor& rProc ) { resolveUnderlines(rProc); } void PageElement::resolveUnderlines( PDFIProcessor& rProc ) { // FIXME: currently the algorithm used is quadratic // this could be solved by some sorting beforehand std::list< Element* >::iterator poly_it = Children.begin(); while( poly_it != Children.end() ) { PolyPolyElement* pPoly = dynamic_cast< PolyPolyElement* >(*poly_it); if( ! pPoly || ! pPoly->Children.empty() ) { ++poly_it; continue; } /* check for: no filling * only two points (FIXME: handle small rectangles, too) * y coordinates of points are equal */ if( pPoly->Action != PATH_STROKE ) { ++poly_it; continue; } if( pPoly->PolyPoly.count() != 1 ) { ++poly_it; continue; } bool bRemovePoly = false; basegfx::B2DPolygon aPoly = pPoly->PolyPoly.getB2DPolygon(0); if( aPoly.count() != 2 || aPoly.getB2DPoint(0).getY() != aPoly.getB2DPoint(1).getY() ) { ++poly_it; continue; } double l_x = aPoly.getB2DPoint(0).getX(); double r_x = aPoly.getB2DPoint(1).getX(); double u_y; if( r_x < l_x ) { u_y = r_x; r_x = l_x; l_x = u_y; } u_y = aPoly.getB2DPoint(0).getY(); for( std::list< Element*>::iterator it = Children.begin(); it != Children.end(); ++it ) { Element* pEle = *it; if( pEle->y <= u_y && pEle->y + pEle->h*1.1 >= u_y ) { // first: is the element underlined completely ? if( pEle->x + pEle->w*0.1 >= l_x && pEle->x + pEle->w*0.9 <= r_x ) { TextElement* pText = dynamic_cast< TextElement* >(pEle); if( pText ) { const GraphicsContext& rTextGC = rProc.getGraphicsContext( pText->GCId ); if( ! rTextGC.isRotatedOrSkewed() ) { bRemovePoly = true; // retrieve ID for modified font FontAttributes aAttr = rProc.getFont( pText->FontId ); aAttr.isUnderline = true; pText->FontId = rProc.getFontId( aAttr ); } } else if( dynamic_cast< HyperlinkElement* >(pEle) ) bRemovePoly = true; } // second: hyperlinks may be larger than their underline // since they are just arbitrary rectangles in the action definition else if( dynamic_cast< HyperlinkElement* >(pEle) != NULL && l_x >= pEle->x && r_x <= pEle->x+pEle->w ) { bRemovePoly = true; } } } if( bRemovePoly ) { std::list< Element* >::iterator next_it = poly_it; ++next_it; Children.erase( poly_it ); delete pPoly; poly_it = next_it; } else ++poly_it; } } DocumentElement::~DocumentElement() { } void DocumentElement::visitedBy( ElementTreeVisitor& rVisitor, const std::list< Element* >::const_iterator& rParentIt) { rVisitor.visit(*this, rParentIt); } }