/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * 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 .
*/ #include <sal/log.hxx> #include <pdfiprocessor.hxx> #include <xmlemitter.hxx> #include <pdfihelper.hxx> #include <imagecontainer.hxx> #include"style.hxx" #include"drawtreevisiting.hxx" #include <genericelements.hxx>
if (isRTL) // If so, reverse string
{ // First, produce mirrored-image for each code point which has the Bidi_Mirrored property.
str = PDFIProcessor::SubstituteBidiMirrored(str); // Then, reverse the code points in the string, in backward order.
str = ::comphelper::string::reverseCodePoints(str);
}
void DrawXmlEmitter::visit( PolyPolyElement& elem, const std::list< std::unique_ptr<Element> >::const_iterator& )
{
elem.updateGeometry(); /* note: * aw recommends using 100dth of mm in all respects since the xml import * (a) is buggy (see issue 37213) * (b) is optimized for 100dth of mm and does not scale itself then, * this does not gain us speed but makes for smaller rounding errors since * the xml importer coordinates are integer based
*/ for (sal_uInt32 i = 0; i< elem.PolyPoly.count(); i++)
{
basegfx::B2DPolygon b2dPolygon = elem.PolyPoly.getB2DPolygon( i );
for ( sal_uInt32 j = 0; j< b2dPolygon.count(); j++ )
{
basegfx::B2DPoint point;
basegfx::B2DPoint nextPoint;
point = b2dPolygon.getB2DPoint( j );
PropertyMap aProps; // PDFIProcessor transforms geometrical objects, not images and text // so we need to tell fillFrameProps here that the transformation for // a PolyPolyElement was already applied (aside from translation)
fillFrameProps( elem, aProps, m_rEmitContext, true );
aProps[ u"svg:viewBox"_ustr ] = "0 0 "
+ OUString::number( convPx2mmPrec2(elem.w)*100.0 )
+ " "
+ OUString::number( convPx2mmPrec2(elem.h)*100.0 );
aProps[ u"svg:d"_ustr ] = basegfx::utils::exportToSvgD( elem.PolyPoly, false, true, false );
void DrawXmlOptimizer::visit( PolyPolyElement& elem, const std::list< std::unique_ptr<Element> >::const_iterator& elemIt )
{ /* note: optimize two consecutive PolyPolyElements that * have the same path but one of which is a stroke while * the other is a fill
*/ if( !elem.Parent ) return;
// find following PolyPolyElement in parent's children list if( elemIt == elem.Parent->Children.end() ) return; auto next_it = elemIt;
++next_it; if( next_it == elem.Parent->Children.end() ) return;
PolyPolyElement* pNext = dynamic_cast<PolyPolyElement*>(next_it->get()); // TODO(F2): this comparison fails for OOo-generated polygons with beziers. if( !pNext || pNext->PolyPoly != elem.PolyPoly ) return;
bool bInsertToParagraph = false; // first check if this is either inside the paragraph if( pCurPara && pDraw->y < pCurPara->y + pCurPara->h )
{ if( pDraw->h < fCurLineHeight * 1.5 )
{
bInsertToParagraph = true;
fCurLineHeight = (fCurLineHeight*double(nCurLineElements) + pDraw->h)/double(nCurLineElements+1);
nCurLineElements++; // mark draw element as character
pDraw->isCharacter = true;
}
} // or perhaps the draw element begins a new paragraph elseif( next_page_element != elem.Children.end() )
{
TextElement* pText = (*next_page_element)->dynCastAsTextElement(); if( ! pText )
{
ParagraphElement* pPara = dynamic_cast<ParagraphElement*>(next_page_element->get()); if( pPara && ! pPara->Children.empty() )
pText = pPara->Children.front()->dynCastAsTextElement();
} if( pText && // check there is a text
pDraw->h < pText->h*1.5 && // and it is approx the same height // and either upper or lower edge of pDraw is inside text's vertical range
( ( pDraw->y >= pText->y && pDraw->y <= pText->y+pText->h ) ||
( pDraw->y+pDraw->h >= pText->y && pDraw->y+pDraw->h <= pText->y+pText->h )
)
)
{
bInsertToParagraph = true;
fCurLineHeight = pDraw->h;
nCurLineElements = 1;
line_left = pDraw->x;
line_right = pDraw->x + pDraw->w; // begin a new paragraph
pCurPara = nullptr; // mark draw element as character
pDraw->isCharacter = true;
}
}
TextElement* pText = (*page_element)->dynCastAsTextElement(); if( ! pText && pLink && ! pLink->Children.empty() )
pText = pLink->Children.front()->dynCastAsTextElement(); if( pText )
{
Element* pGeo = pLink ? static_cast<Element*>(pLink) : static_cast<Element*>(pText); if( pCurPara )
{ // there was already a text element, check for a new paragraph if( nCurLineElements > 0 )
{ // if the new text is significantly distant from the paragraph // begin a new paragraph if( pGeo->y > pCurPara->y + pCurPara->h + fCurLineHeight*0.5 )
pCurPara = nullptr; // insert new paragraph elseif( pGeo->y > (pCurPara->y+pCurPara->h - fCurLineHeight*0.05) )
{ // new paragraph if either the last line of the paragraph // was significantly shorter than the paragraph as a whole if( (line_right - line_left) < pCurPara->w*0.75 )
pCurPara = nullptr; // or the last line was significantly smaller than the column width elseif( (line_right - line_left) < column_width*0.75 )
pCurPara = nullptr;
}
}
// move element to current paragraph if (! pCurPara ) // new paragraph, insert one
{
pCurPara = ElementFactory::createParagraphElement( nullptr );
assert(pCurPara); // set parent
pCurPara->Parent = &elem; //insert new paragraph before current element
page_element = elem.Children.insert( page_element, std::unique_ptr<Element>(pCurPara) ); // forward iterator to current element again
++ page_element; // update next_element which is now invalid
next_page_element = page_element;
++ next_page_element;
}
Element* pCurEle = page_element->get();
Element::setParent( page_element, pCurPara );
OSL_ENSURE( !pText || pCurEle == pText || pCurEle == pLink, "paragraph child list in disorder" ); if( pText || pDraw )
pCurPara->updateGeometryWith( pCurEle );
}
// process children
elem.applyToChildren(*this);
}
staticbool isSpaces(TextElement* pTextElem)
{ for (sal_Int32 i = 0; i != pTextElem->Text.getLength(); ++i) { if (pTextElem->Text[i] != ' ') { returnfalse;
}
} returntrue;
}
void DrawXmlOptimizer::optimizeTextElements(Element& rParent)
{ if( rParent.Children.empty() ) // this should not happen
{
OSL_FAIL( "empty paragraph optimized" ); return;
}
// concatenate child elements with same font id auto next = rParent.Children.begin(); auto it = next++;
// line and space optimization; works only in strictly horizontal mode
// concatenate consecutive text elements unless there is a // font or text color change, leave a new span in that case if( (pCur->FontId == pNext->FontId || isSpaces(pNext)) &&
rCurGC.FillColor.Red == rNextGC.FillColor.Red &&
rCurGC.FillColor.Green == rNextGC.FillColor.Green &&
rCurGC.FillColor.Blue == rNextGC.FillColor.Blue &&
rCurGC.FillColor.Alpha == rNextGC.FillColor.Alpha
)
{
pCur->updateGeometryWith( pNext ); if (pPara && pPara->bRtl)
{ // Tdf#152083: If RTL, reverse the text in pNext so that its correct order is // restored when the combined text is reversed in DrawXmlEmitter::visit.
OUString tempStr; bool bNeedReverse=false;
str = pNext->Text.toString(); for (sal_Int32 i=0; i < str.getLength(); i++)
{ if (str[i] == u' ')
{ // Space char (e.g. the space as in " م") needs special treatment. // First, append the space char to pCur.
pCur->Text.append(OUStringChar(str[i])); // Then, check whether the tmpStr needs reverse, if so then reverse and append. if (bNeedReverse)
{
tempStr = ::comphelper::string::reverseCodePoints(tempStr);
pCur->Text.append(tempStr);
tempStr = u""_ustr;
}
bNeedReverse = false;
} else
{
tempStr += OUStringChar(str[i]);
bNeedReverse = true;
}
} // Do the last append if (bNeedReverse)
{
tempStr = ::comphelper::string::reverseCodePoints(tempStr);
pCur->Text.append(tempStr);
} else
{
pCur->Text.append(tempStr);
}
} else
{ // append text to current element directly without reverse
pCur->Text.append( pNext->Text );
}
if (bPara && pPara && isComplex(GetBreakIterator(), pCur))
pPara->bRtl = true; // append eventual children to current element // and clear children (else the children just // appended to pCur would be destroyed)
pCur->Children.splice( pCur->Children.end(), pNext->Children ); // get rid of the now useless element
rParent.Children.erase( next );
bConcat = true;
}
}
} elseif( dynamic_cast<HyperlinkElement*>(it->get()) )
optimizeTextElements( **it ); if ( bConcat )
next = it; else
++it;
++next;
}
}
// family name // TODO: tdf#143095: use system font name rather than PSName
SAL_INFO("sdext.pdfimport", "The font used in xml is: " << rFont.familyName);
aFontProps[ u"fo:font-family"_ustr ] = rFont.familyName;
aFontProps[ u"style:font-family-asian"_ustr ] = rFont.familyName;
aFontProps[ u"style:font-family-complex"_ustr ] = rFont.familyName;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.