Line data Source code
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 "style.hxx"
26 : #include "drawtreevisiting.hxx"
27 : #include "genericelements.hxx"
28 :
29 : #include "basegfx/polygon/b2dpolypolygontools.hxx"
30 : #include "basegfx/range/b2drange.hxx"
31 :
32 : #include "com/sun/star/i18n/BreakIterator.hpp"
33 : #include "com/sun/star/i18n/CharacterClassification.hpp"
34 : #include "com/sun/star/lang/XMultiServiceFactory.hpp"
35 : #include "comphelper/processfactory.hxx"
36 : #include "com/sun/star/i18n/ScriptType.hpp"
37 : #include "com/sun/star/i18n/DirectionProperty.hpp"
38 :
39 : #include <string.h>
40 :
41 : using namespace ::com::sun::star;
42 : using namespace ::com::sun::star::lang;
43 : using namespace ::com::sun::star::i18n;
44 : using namespace ::com::sun::star::uno;
45 :
46 : namespace pdfi
47 : {
48 :
49 1503 : const Reference< XBreakIterator >& DrawXmlOptimizer::GetBreakIterator()
50 : {
51 1503 : if ( !mxBreakIter.is() )
52 : {
53 1 : Reference< XComponentContext > xContext( this->m_rProcessor.m_xContext, uno::UNO_SET_THROW );
54 1 : mxBreakIter = BreakIterator::create(xContext);
55 : }
56 1503 : return mxBreakIter;
57 : }
58 :
59 11 : const Reference< XCharacterClassification >& DrawXmlEmitter::GetCharacterClassification()
60 : {
61 11 : if ( !mxCharClass.is() )
62 : {
63 1 : Reference< XComponentContext > xContext( m_rEmitContext.m_xContext, uno::UNO_SET_THROW );
64 1 : mxCharClass = CharacterClassification::create(xContext);
65 : }
66 11 : return mxCharClass;
67 : }
68 :
69 0 : void DrawXmlEmitter::visit( HyperlinkElement& elem, const std::list< Element* >::const_iterator& )
70 : {
71 0 : if( elem.Children.empty() )
72 0 : return;
73 :
74 0 : const char* pType = dynamic_cast<DrawElement*>(elem.Children.front()) ? "draw:a" : "text:a";
75 :
76 0 : PropertyMap aProps;
77 0 : aProps[ "xlink:type" ] = "simple";
78 0 : aProps[ "xlink:href" ] = elem.URI;
79 0 : aProps[ "office:target-frame-name" ] = "_blank";
80 0 : aProps[ "xlink:show" ] = "new";
81 :
82 0 : m_rEmitContext.rEmitter.beginTag( pType, aProps );
83 0 : std::list< Element* >::iterator this_it = elem.Children.begin();
84 0 : while( this_it !=elem.Children.end() && *this_it != &elem )
85 : {
86 0 : (*this_it)->visitedBy( *this, this_it );
87 0 : ++this_it;
88 : }
89 0 : m_rEmitContext.rEmitter.endTag( pType );
90 : }
91 :
92 11 : void DrawXmlEmitter::visit( TextElement& elem, const std::list< Element* >::const_iterator& )
93 : {
94 11 : if( elem.Text.isEmpty() )
95 11 : return;
96 :
97 11 : OUString strSpace(32);
98 22 : OUString strNbSpace(160);
99 22 : OUString tabSpace(0x09);
100 22 : PropertyMap aProps;
101 11 : if( elem.StyleId != -1 )
102 : {
103 22 : aProps[ OUString( "text:style-name" ) ] =
104 11 : m_rEmitContext.rStyles.getStyleName( elem.StyleId );
105 : }
106 :
107 22 : OUString str(elem.Text.getStr());
108 :
109 : // Check for RTL
110 11 : bool isRTL = false;
111 22 : Reference< i18n::XCharacterClassification > xCC( GetCharacterClassification() );
112 11 : if( xCC.is() )
113 : {
114 104 : for(int i=1; i< elem.Text.getLength(); i++)
115 : {
116 93 : sal_Int16 nType = xCC->getCharacterDirection( str, i );
117 93 : if ( nType == ::com::sun::star::i18n::DirectionProperty_RIGHT_TO_LEFT ||
118 93 : nType == ::com::sun::star::i18n::DirectionProperty_RIGHT_TO_LEFT_ARABIC ||
119 93 : nType == ::com::sun::star::i18n::DirectionProperty_RIGHT_TO_LEFT_EMBEDDING ||
120 : nType == ::com::sun::star::i18n::DirectionProperty_RIGHT_TO_LEFT_OVERRIDE
121 : )
122 0 : isRTL = true;
123 : }
124 : }
125 :
126 11 : if (isRTL) // If so, reverse string
127 0 : str = m_rProcessor.mirrorString( str );
128 :
129 11 : m_rEmitContext.rEmitter.beginTag( "text:span", aProps );
130 :
131 115 : for(int i=0; i< elem.Text.getLength(); i++)
132 : {
133 104 : OUString strToken= str.copy(i,1) ;
134 104 : if( strSpace.equals(strToken) || strNbSpace.equals(strToken))
135 : {
136 14 : aProps[ "text:c" ] = "1";
137 14 : m_rEmitContext.rEmitter.beginTag( "text:s", aProps );
138 14 : m_rEmitContext.rEmitter.endTag( "text:s");
139 : }
140 : else
141 : {
142 90 : if( tabSpace.equals(strToken) )
143 : {
144 0 : m_rEmitContext.rEmitter.beginTag( "text:tab", aProps );
145 0 : m_rEmitContext.rEmitter.endTag( "text:tab");
146 : }
147 : else
148 : {
149 90 : m_rEmitContext.rEmitter.write( strToken );
150 : }
151 : }
152 104 : }
153 :
154 11 : std::list< Element* >::iterator this_it = elem.Children.begin();
155 22 : while( this_it !=elem.Children.end() && *this_it != &elem )
156 : {
157 0 : (*this_it)->visitedBy( *this, this_it );
158 0 : ++this_it;
159 : }
160 :
161 22 : m_rEmitContext.rEmitter.endTag( "text:span" );
162 : }
163 :
164 11 : void DrawXmlEmitter::visit( ParagraphElement& elem, const std::list< Element* >::const_iterator& )
165 : {
166 11 : PropertyMap aProps;
167 11 : if( elem.StyleId != -1 )
168 : {
169 11 : aProps[ "text:style-name" ] = m_rEmitContext.rStyles.getStyleName( elem.StyleId );
170 : }
171 11 : const char* pTagType = "text:p";
172 11 : if( elem.Type == elem.Headline )
173 0 : pTagType = "text:h";
174 11 : m_rEmitContext.rEmitter.beginTag( pTagType, aProps );
175 :
176 11 : std::list< Element* >::iterator this_it = elem.Children.begin();
177 33 : while( this_it !=elem.Children.end() && *this_it != &elem )
178 : {
179 11 : (*this_it)->visitedBy( *this, this_it );
180 11 : ++this_it;
181 : }
182 :
183 11 : m_rEmitContext.rEmitter.endTag( pTagType );
184 11 : }
185 :
186 15 : void DrawXmlEmitter::fillFrameProps( DrawElement& rElem,
187 : PropertyMap& rProps,
188 : const EmitContext& rEmitContext,
189 : bool bWasTransformed
190 : )
191 : {
192 15 : double rel_x = rElem.x, rel_y = rElem.y;
193 :
194 15 : rProps[ "draw:z-index" ] = OUString::valueOf( rElem.ZOrder );
195 15 : rProps[ "draw:style-name"] = rEmitContext.rStyles.getStyleName( rElem.StyleId );
196 15 : rProps[ "svg:width" ] = convertPixelToUnitString( rElem.w );
197 15 : rProps[ "svg:height" ] = convertPixelToUnitString( rElem.h );
198 :
199 : const GraphicsContext& rGC =
200 15 : rEmitContext.rProcessor.getGraphicsContext( rElem.GCId );
201 15 : if( rGC.Transformation.isIdentity() || bWasTransformed )
202 : {
203 3 : rProps[ "svg:x" ] = convertPixelToUnitString( rel_x );
204 3 : rProps[ "svg:y" ] = convertPixelToUnitString( rel_y );
205 : }
206 : else
207 : {
208 24 : basegfx::B2DTuple aScale, aTranslation;
209 : double fRotate, fShearX;
210 :
211 12 : rGC.Transformation.decompose( aScale, aTranslation, fRotate, fShearX );
212 :
213 24 : OUStringBuffer aBuf( 256 );
214 :
215 : // TODO(F2): general transformation case missing; if implemented, note
216 : // that ODF rotation is oriented the other way
217 :
218 : // vertical mirroring is done by horizontally mirroring and rotaing 180 degree
219 : // quaint !
220 12 : if( rElem.MirrorVertical )
221 0 : fRotate += M_PI;
222 :
223 : // build transformation string
224 12 : if( fShearX != 0.0 )
225 : {
226 0 : aBuf.appendAscii( "skewX( " );
227 0 : aBuf.append( fShearX );
228 0 : aBuf.appendAscii( " )" );
229 : }
230 12 : if( fRotate != 0.0 )
231 : {
232 0 : if( !aBuf.isEmpty() )
233 0 : aBuf.append( sal_Unicode(' ') );
234 0 : aBuf.appendAscii( "rotate( " );
235 0 : aBuf.append( -fRotate );
236 0 : aBuf.appendAscii( " )" );
237 :
238 : }
239 12 : if( !aBuf.isEmpty() )
240 0 : aBuf.append( sal_Unicode(' ') );
241 12 : aBuf.appendAscii( "translate( " );
242 12 : aBuf.append( convertPixelToUnitString( rel_x ) );
243 12 : aBuf.append( sal_Unicode(' ') );
244 12 : aBuf.append( convertPixelToUnitString( rel_y ) );
245 12 : aBuf.appendAscii( " )" );
246 :
247 24 : rProps[ "draw:transform" ] = aBuf.makeStringAndClear();
248 : }
249 15 : }
250 :
251 12 : void DrawXmlEmitter::visit( FrameElement& elem, const std::list< Element* >::const_iterator& )
252 : {
253 12 : if( elem.Children.empty() )
254 12 : return;
255 :
256 12 : bool bTextBox = (dynamic_cast<ParagraphElement*>(elem.Children.front()) != NULL);
257 12 : PropertyMap aFrameProps;
258 12 : fillFrameProps( elem, aFrameProps, m_rEmitContext );
259 12 : m_rEmitContext.rEmitter.beginTag( "draw:frame", aFrameProps );
260 12 : if( bTextBox )
261 11 : m_rEmitContext.rEmitter.beginTag( "draw:text-box", PropertyMap() );
262 :
263 12 : std::list< Element* >::iterator this_it = elem.Children.begin();
264 36 : while( this_it !=elem.Children.end() && *this_it != &elem )
265 : {
266 12 : (*this_it)->visitedBy( *this, this_it );
267 12 : ++this_it;
268 : }
269 :
270 12 : if( bTextBox )
271 11 : m_rEmitContext.rEmitter.endTag( "draw:text-box" );
272 12 : m_rEmitContext.rEmitter.endTag( "draw:frame" );
273 : }
274 :
275 3 : void DrawXmlEmitter::visit( PolyPolyElement& elem, const std::list< Element* >::const_iterator& )
276 : {
277 3 : elem.updateGeometry();
278 : /* note:
279 : * aw recommends using 100dth of mm in all respects since the xml import
280 : * (a) is buggy (see issue 37213)
281 : * (b) is optimized for 100dth of mm and does not scale itself then,
282 : * this does not gain us speed but makes for smaller rounding errors since
283 : * the xml importer coordinates are integer based
284 : */
285 6 : for (sal_uInt32 i = 0; i< elem.PolyPoly.count(); i++)
286 : {
287 3 : basegfx::B2DPolygon b2dPolygon;
288 3 : b2dPolygon = elem.PolyPoly.getB2DPolygon( i );
289 :
290 12 : for ( sal_uInt32 j = 0; j< b2dPolygon.count(); j++ )
291 : {
292 9 : basegfx::B2DPoint point;
293 18 : basegfx::B2DPoint nextPoint;
294 9 : point = b2dPolygon.getB2DPoint( j );
295 :
296 18 : basegfx::B2DPoint prevPoint;
297 9 : prevPoint = b2dPolygon.getPrevControlPoint( j ) ;
298 :
299 9 : point.setX( convPx2mmPrec2( point.getX() )*100.0 );
300 9 : point.setY( convPx2mmPrec2( point.getY() )*100.0 );
301 :
302 9 : if ( b2dPolygon.isPrevControlPointUsed( j ) )
303 : {
304 4 : prevPoint.setX( convPx2mmPrec2( prevPoint.getX() )*100.0 );
305 4 : prevPoint.setY( convPx2mmPrec2( prevPoint.getY() )*100.0 );
306 : }
307 :
308 9 : if ( b2dPolygon.isNextControlPointUsed( j ) )
309 : {
310 4 : nextPoint = b2dPolygon.getNextControlPoint( j ) ;
311 4 : nextPoint.setX( convPx2mmPrec2( nextPoint.getX() )*100.0 );
312 4 : nextPoint.setY( convPx2mmPrec2( nextPoint.getY() )*100.0 );
313 : }
314 :
315 9 : b2dPolygon.setB2DPoint( j, point );
316 :
317 9 : if ( b2dPolygon.isPrevControlPointUsed( j ) )
318 4 : b2dPolygon.setPrevControlPoint( j , prevPoint ) ;
319 :
320 9 : if ( b2dPolygon.isNextControlPointUsed( j ) )
321 4 : b2dPolygon.setNextControlPoint( j , nextPoint ) ;
322 9 : }
323 :
324 3 : elem.PolyPoly.setB2DPolygon( i, b2dPolygon );
325 3 : }
326 :
327 3 : PropertyMap aProps;
328 : // PDFIProcessor transforms geometrical objects, not images and text
329 : // so we need to tell fillFrameProps here that the transformation for
330 : // a PolyPolyElement was already applied (aside form translation)
331 3 : fillFrameProps( elem, aProps, m_rEmitContext, true );
332 6 : OUStringBuffer aBuf( 64 );
333 3 : aBuf.appendAscii( "0 0 " );
334 3 : aBuf.append( convPx2mmPrec2(elem.w)*100.0 );
335 3 : aBuf.append( sal_Unicode(' ') );
336 3 : aBuf.append( convPx2mmPrec2(elem.h)*100.0 );
337 3 : aProps[ "svg:viewBox" ] = aBuf.makeStringAndClear();
338 3 : aProps[ "svg:d" ] = basegfx::tools::exportToSvgD( elem.PolyPoly );
339 :
340 3 : m_rEmitContext.rEmitter.beginTag( "draw:path", aProps );
341 6 : m_rEmitContext.rEmitter.endTag( "draw:path" );
342 3 : }
343 :
344 1 : void DrawXmlEmitter::visit( ImageElement& elem, const std::list< Element* >::const_iterator& )
345 : {
346 1 : PropertyMap aImageProps;
347 1 : m_rEmitContext.rEmitter.beginTag( "draw:image", aImageProps );
348 1 : m_rEmitContext.rEmitter.beginTag( "office:binary-data", PropertyMap() );
349 1 : m_rEmitContext.rImages.writeBase64EncodedStream( elem.Image, m_rEmitContext);
350 1 : m_rEmitContext.rEmitter.endTag( "office:binary-data" );
351 1 : m_rEmitContext.rEmitter.endTag( "draw:image" );
352 1 : }
353 :
354 1 : void DrawXmlEmitter::visit( PageElement& elem, const std::list< Element* >::const_iterator& )
355 : {
356 1 : PropertyMap aPageProps;
357 1 : aPageProps[ "draw:master-page-name" ] = m_rEmitContext.rStyles.getStyleName( elem.StyleId );
358 :
359 1 : m_rEmitContext.rEmitter.beginTag("draw:page", aPageProps);
360 :
361 1 : if( m_rEmitContext.xStatusIndicator.is() )
362 0 : m_rEmitContext.xStatusIndicator->setValue( elem.PageNumber );
363 :
364 1 : std::list< Element* >::iterator this_it = elem.Children.begin();
365 17 : while( this_it !=elem.Children.end() && *this_it != &elem )
366 : {
367 15 : (*this_it)->visitedBy( *this, this_it );
368 15 : ++this_it;
369 : }
370 :
371 1 : m_rEmitContext.rEmitter.endTag("draw:page");
372 1 : }
373 :
374 1 : void DrawXmlEmitter::visit( DocumentElement& elem, const std::list< Element* >::const_iterator&)
375 : {
376 1 : m_rEmitContext.rEmitter.beginTag( "office:body", PropertyMap() );
377 : m_rEmitContext.rEmitter.beginTag( m_bWriteDrawDocument ? "office:drawing" : "office:presentation",
378 1 : PropertyMap() );
379 :
380 1 : std::list< Element* >::iterator this_it = elem.Children.begin();
381 3 : while( this_it !=elem.Children.end() && *this_it != &elem )
382 : {
383 1 : (*this_it)->visitedBy( *this, this_it );
384 1 : ++this_it;
385 : }
386 :
387 1 : m_rEmitContext.rEmitter.endTag( m_bWriteDrawDocument ? "office:drawing" : "office:presentation" );
388 1 : m_rEmitContext.rEmitter.endTag( "office:body" );
389 1 : }
390 :
391 : /////////////////////////////////////////////////////////////////
392 :
393 0 : void DrawXmlOptimizer::visit( HyperlinkElement&, const std::list< Element* >::const_iterator& )
394 : {
395 0 : }
396 :
397 11 : void DrawXmlOptimizer::visit( TextElement&, const std::list< Element* >::const_iterator&)
398 : {
399 11 : }
400 :
401 12 : void DrawXmlOptimizer::visit( FrameElement& elem, const std::list< Element* >::const_iterator& )
402 : {
403 12 : elem.applyToChildren(*this);
404 12 : }
405 :
406 1 : void DrawXmlOptimizer::visit( ImageElement&, const std::list< Element* >::const_iterator& )
407 : {
408 1 : }
409 :
410 3 : void DrawXmlOptimizer::visit( PolyPolyElement& elem, const std::list< Element* >::const_iterator& )
411 : {
412 : /* note: optimize two consecutive PolyPolyElements that
413 : * have the same path but one of which is a stroke while
414 : * the other is a fill
415 : */
416 3 : if( elem.Parent )
417 : {
418 : // find following PolyPolyElement in parent's children list
419 3 : std::list< Element* >::iterator this_it = elem.Parent->Children.begin();
420 22 : while( this_it != elem.Parent->Children.end() && *this_it != &elem )
421 16 : ++this_it;
422 :
423 3 : if( this_it != elem.Parent->Children.end() )
424 : {
425 3 : std::list< Element* >::iterator next_it = this_it;
426 3 : if( ++next_it != elem.Parent->Children.end() )
427 : {
428 3 : PolyPolyElement* pNext = dynamic_cast<PolyPolyElement*>(*next_it);
429 :
430 : // TODO(F2): this comparison fails for OOo-generated polygons with beziers.
431 3 : if( pNext && pNext->PolyPoly == elem.PolyPoly )
432 : {
433 : const GraphicsContext& rNextGC =
434 0 : m_rProcessor.getGraphicsContext( pNext->GCId );
435 : const GraphicsContext& rThisGC =
436 0 : m_rProcessor.getGraphicsContext( elem.GCId );
437 :
438 0 : if( rThisGC.BlendMode == rNextGC.BlendMode &&
439 0 : rThisGC.Flatness == rNextGC.Flatness &&
440 0 : rThisGC.Transformation == rNextGC.Transformation &&
441 0 : rThisGC.Clip == rNextGC.Clip &&
442 0 : rThisGC.FillColor.Red == rNextGC.FillColor.Red &&
443 0 : rThisGC.FillColor.Green== rNextGC.FillColor.Green &&
444 0 : rThisGC.FillColor.Blue == rNextGC.FillColor.Blue &&
445 0 : rThisGC.FillColor.Alpha== rNextGC.FillColor.Alpha &&
446 0 : pNext->Action == PATH_STROKE &&
447 0 : (elem.Action == PATH_FILL || elem.Action == PATH_EOFILL) )
448 : {
449 0 : GraphicsContext aGC = rThisGC;
450 0 : aGC.LineJoin = rNextGC.LineJoin;
451 0 : aGC.LineCap = rNextGC.LineCap;
452 0 : aGC.LineWidth = rNextGC.LineWidth;
453 0 : aGC.MiterLimit= rNextGC.MiterLimit;
454 0 : aGC.DashArray = rNextGC.DashArray;
455 0 : aGC.LineColor = rNextGC.LineColor;
456 0 : elem.GCId = m_rProcessor.getGCId( aGC );
457 :
458 0 : elem.Action |= pNext->Action;
459 :
460 0 : elem.Children.splice( elem.Children.end(), pNext->Children );
461 0 : elem.Parent->Children.erase( next_it );
462 0 : delete pNext;
463 : }
464 : }
465 : }
466 : }
467 : }
468 3 : }
469 :
470 11 : void DrawXmlOptimizer::visit( ParagraphElement& elem, const std::list< Element* >::const_iterator& )
471 : {
472 11 : optimizeTextElements( elem );
473 :
474 11 : elem.applyToChildren(*this);
475 11 : }
476 :
477 1 : void DrawXmlOptimizer::visit( PageElement& elem, const std::list< Element* >::const_iterator& )
478 : {
479 1 : if( m_rProcessor.getStatusIndicator().is() )
480 0 : m_rProcessor.getStatusIndicator()->setValue( elem.PageNumber );
481 :
482 : // resolve hyperlinks
483 1 : elem.resolveHyperlinks();
484 :
485 1 : elem.resolveFontStyles( m_rProcessor ); // underlines and such
486 :
487 : // FIXME: until hyperlinks and font effects are adjusted for
488 : // geometrical search handle them before sorting
489 1 : m_rProcessor.sortElements( &elem );
490 :
491 : // find paragraphs in text
492 1 : ParagraphElement* pCurPara = NULL;
493 1 : std::list< Element* >::iterator page_element, next_page_element;
494 1 : next_page_element = elem.Children.begin();
495 1 : double fCurLineHeight = 0.0; // average height of text items in current para
496 1 : int nCurLineElements = 0; // number of line contributing elements in current para
497 1 : double line_left = elem.w, line_right = 0.0;
498 1 : double column_width = elem.w*0.75; // estimate text width
499 : // TODO: guess columns
500 17 : while( next_page_element != elem.Children.end() )
501 : {
502 15 : page_element = next_page_element++;
503 15 : ParagraphElement* pPagePara = dynamic_cast<ParagraphElement*>(*page_element);
504 15 : if( pPagePara )
505 : {
506 0 : pCurPara = pPagePara;
507 : // adjust line height and text items
508 0 : fCurLineHeight = 0.0;
509 0 : nCurLineElements = 0;
510 0 : for( std::list< Element* >::iterator it = pCurPara->Children.begin();
511 0 : it != pCurPara->Children.end(); ++it )
512 : {
513 0 : TextElement* pTestText = dynamic_cast<TextElement*>(*it);
514 0 : if( pTestText )
515 : {
516 0 : fCurLineHeight = (fCurLineHeight*double(nCurLineElements) + pTestText->h)/double(nCurLineElements+1);
517 0 : nCurLineElements++;
518 : }
519 : }
520 0 : continue;
521 : }
522 :
523 15 : HyperlinkElement* pLink = dynamic_cast<HyperlinkElement*>(*page_element);
524 15 : DrawElement* pDraw = dynamic_cast<DrawElement*>(*page_element);
525 15 : if( ! pDraw && pLink && ! pLink->Children.empty() )
526 0 : pDraw = dynamic_cast<DrawElement*>(pLink->Children.front() );
527 15 : if( pDraw )
528 : {
529 : // insert small drawing objects as character, else leave them page bound
530 :
531 15 : bool bInsertToParagraph = false;
532 : // first check if this is either inside the paragraph
533 15 : if( pCurPara && pDraw->y < pCurPara->y + pCurPara->h )
534 : {
535 0 : if( pDraw->h < fCurLineHeight * 1.5 )
536 : {
537 0 : bInsertToParagraph = true;
538 0 : fCurLineHeight = (fCurLineHeight*double(nCurLineElements) + pDraw->h)/double(nCurLineElements+1);
539 0 : nCurLineElements++;
540 : // mark draw element as character
541 0 : pDraw->isCharacter = true;
542 : }
543 : }
544 : // or perhaps the draw element begins a new paragraph
545 15 : else if( next_page_element != elem.Children.end() )
546 : {
547 14 : TextElement* pText = dynamic_cast<TextElement*>(*next_page_element);
548 14 : if( ! pText )
549 : {
550 14 : ParagraphElement* pPara = dynamic_cast<ParagraphElement*>(*next_page_element);
551 14 : if( pPara && ! pPara->Children.empty() )
552 0 : pText = dynamic_cast<TextElement*>(pPara->Children.front());
553 : }
554 14 : if( pText && // check there is a text
555 0 : pDraw->h < pText->h*1.5 && // and it is approx the same height
556 : // and either upper or lower edge of pDraw is inside text's vertical range
557 0 : ( ( pDraw->y >= pText->y && pDraw->y <= pText->y+pText->h ) ||
558 0 : ( pDraw->y+pDraw->h >= pText->y && pDraw->y+pDraw->h <= pText->y+pText->h )
559 : )
560 : )
561 : {
562 0 : bInsertToParagraph = true;
563 0 : fCurLineHeight = pDraw->h;
564 0 : nCurLineElements = 1;
565 0 : line_left = pDraw->x;
566 0 : line_right = pDraw->x + pDraw->w;
567 : // begin a new paragraph
568 0 : pCurPara = NULL;
569 : // mark draw element as character
570 0 : pDraw->isCharacter = true;
571 : }
572 : }
573 :
574 15 : if( ! bInsertToParagraph )
575 : {
576 15 : pCurPara = NULL;
577 15 : continue;
578 : }
579 : }
580 :
581 0 : TextElement* pText = dynamic_cast<TextElement*>(*page_element);
582 0 : if( ! pText && pLink && ! pLink->Children.empty() )
583 0 : pText = dynamic_cast<TextElement*>(pLink->Children.front());
584 0 : if( pText )
585 : {
586 : Element* pGeo = pLink ? static_cast<Element*>(pLink) :
587 0 : static_cast<Element*>(pText);
588 0 : if( pCurPara )
589 : {
590 : // there was already a text element, check for a new paragraph
591 0 : if( nCurLineElements > 0 )
592 : {
593 : // if the new text is significantly distant from the paragraph
594 : // begin a new paragraph
595 0 : if( pGeo->y > pCurPara->y + pCurPara->h + fCurLineHeight*0.5 )
596 0 : pCurPara = NULL; // insert new paragraph
597 0 : else if( pGeo->y > (pCurPara->y+pCurPara->h - fCurLineHeight*0.05) )
598 : {
599 : // new paragraph if either the last line of the paragraph
600 : // was significantly shorter than the paragraph as a whole
601 0 : if( (line_right - line_left) < pCurPara->w*0.75 )
602 0 : pCurPara = NULL;
603 : // or the last line was significantly smaller than the column width
604 0 : else if( (line_right - line_left) < column_width*0.75 )
605 0 : pCurPara = NULL;
606 : }
607 : }
608 :
609 :
610 : }
611 :
612 :
613 : // update line height/width
614 0 : if( pCurPara )
615 : {
616 0 : fCurLineHeight = (fCurLineHeight*double(nCurLineElements) + pGeo->h)/double(nCurLineElements+1);
617 0 : nCurLineElements++;
618 0 : if( pGeo->x < line_left )
619 0 : line_left = pGeo->x;
620 0 : if( pGeo->x+pGeo->w > line_right )
621 0 : line_right = pGeo->x+pGeo->w;
622 : }
623 : else
624 : {
625 0 : fCurLineHeight = pGeo->h;
626 0 : nCurLineElements = 1;
627 0 : line_left = pGeo->x;
628 0 : line_right = pGeo->x + pGeo->w;
629 : }
630 : }
631 :
632 :
633 : // move element to current paragraph
634 0 : if (! pCurPara ) // new paragraph, insert one
635 : {
636 0 : pCurPara = m_rProcessor.getElementFactory()->createParagraphElement( NULL );
637 : // set parent
638 0 : pCurPara->Parent = &elem;
639 : //insert new paragraph before current element
640 0 : page_element = elem.Children.insert( page_element, pCurPara );
641 : // forward iterator to current element again
642 0 : ++ page_element;
643 : // update next_element which is now invalid
644 0 : next_page_element = page_element;
645 0 : ++ next_page_element;
646 : }
647 0 : Element* pCurEle = *page_element;
648 0 : pCurEle->setParent( page_element, pCurPara );
649 : OSL_ENSURE( !pText || pCurEle == pText || pCurEle == pLink, "paragraph child list in disorder" );
650 0 : if( pText || pDraw )
651 0 : pCurPara->updateGeometryWith( pCurEle );
652 : }
653 :
654 : // process children
655 1 : elem.applyToChildren(*this);
656 1 : }
657 :
658 0 : bool isSpaces(TextElement* pTextElem)
659 : {
660 0 : for (sal_Int32 i = 0; i != pTextElem->Text.getLength(); ++i) {
661 0 : if (pTextElem->Text[i] != ' ') {
662 0 : return false;
663 : }
664 : }
665 0 : return true;
666 : }
667 :
668 0 : bool notTransformed(const GraphicsContext& GC)
669 : {
670 : return (
671 0 : GC.Transformation.get(0,0) == 100.00 &&
672 0 : GC.Transformation.get(1,0) == 0.00 &&
673 0 : GC.Transformation.get(0,1) == 0.00 &&
674 0 : GC.Transformation.get(1,1) == -100.00
675 0 : );
676 : }
677 :
678 11 : void DrawXmlOptimizer::optimizeTextElements(Element& rParent)
679 : {
680 11 : if( rParent.Children.empty() ) // this should not happen
681 : {
682 : OSL_FAIL( "empty paragraph optimized" );
683 11 : return;
684 : }
685 :
686 : // concatenate child elements with same font id
687 11 : std::list< Element* >::iterator next = rParent.Children.begin();
688 11 : std::list< Element* >::iterator it = next++;
689 :
690 115 : while( next != rParent.Children.end() )
691 : {
692 93 : bool bConcat = false;
693 93 : TextElement* pCur = dynamic_cast<TextElement*>(*it);
694 :
695 93 : if( pCur )
696 : {
697 93 : TextElement* pNext = dynamic_cast<TextElement*>(*next);
698 93 : bool isComplex = false;
699 93 : OUString str(pCur->Text.getStr());
700 798 : for(int i=0; i< str.getLength(); i++)
701 : {
702 705 : sal_Int16 nType = GetBreakIterator()->getScriptType( str, i );
703 705 : if (nType == ::com::sun::star::i18n::ScriptType::COMPLEX)
704 0 : isComplex = true;
705 : }
706 93 : bool bPara = strspn("ParagraphElement", typeid(rParent).name());
707 93 : ParagraphElement* pPara = dynamic_cast<ParagraphElement*>(&rParent);
708 93 : if (bPara && isComplex)
709 0 : pPara->bRtl = true;
710 93 : if( pNext )
711 : {
712 93 : const GraphicsContext& rCurGC = m_rProcessor.getGraphicsContext( pCur->GCId );
713 93 : const GraphicsContext& rNextGC = m_rProcessor.getGraphicsContext( pNext->GCId );
714 :
715 : // line and space optimization; works only in strictly horizontal mode
716 :
717 : // concatenate consecutive text elements unless there is a
718 : // font or text color or matrix change, leave a new span in that case
719 279 : if( (pCur->FontId == pNext->FontId || isSpaces(pNext)) &&
720 186 : rCurGC.FillColor.Red == rNextGC.FillColor.Red &&
721 186 : rCurGC.FillColor.Green == rNextGC.FillColor.Green &&
722 186 : rCurGC.FillColor.Blue == rNextGC.FillColor.Blue &&
723 372 : rCurGC.FillColor.Alpha == rNextGC.FillColor.Alpha &&
724 93 : (rCurGC.Transformation == rNextGC.Transformation || notTransformed(rNextGC))
725 : )
726 : {
727 93 : pCur->updateGeometryWith( pNext );
728 : // append text to current element
729 93 : pCur->Text.append( pNext->Text.getStr(), pNext->Text.getLength() );
730 :
731 93 : str = pCur->Text.getStr();
732 891 : for(int i=0; i< str.getLength(); i++)
733 : {
734 798 : sal_Int16 nType = GetBreakIterator()->getScriptType( str, i );
735 798 : if (nType == ::com::sun::star::i18n::ScriptType::COMPLEX)
736 0 : isComplex = true;
737 : }
738 93 : if (bPara && isComplex)
739 0 : pPara->bRtl = true;
740 : // append eventual children to current element
741 : // and clear children (else the children just
742 : // appended to pCur would be destroyed)
743 93 : pCur->Children.splice( pCur->Children.end(), pNext->Children );
744 : // get rid of the now useless element
745 93 : rParent.Children.erase( next );
746 93 : delete pNext;
747 93 : bConcat = true;
748 : }
749 93 : }
750 : }
751 0 : else if( dynamic_cast<HyperlinkElement*>(*it) )
752 0 : optimizeTextElements( **it );
753 93 : if ( bConcat )
754 93 : next = it;
755 : else
756 0 : ++it;
757 93 : ++next;
758 : }
759 : }
760 :
761 1 : void DrawXmlOptimizer::visit( DocumentElement& elem, const std::list< Element* >::const_iterator&)
762 : {
763 1 : elem.applyToChildren(*this);
764 1 : }
765 :
766 : //////////////////////////////////////////////////////////////////////////////////
767 :
768 :
769 3 : void DrawXmlFinalizer::visit( PolyPolyElement& elem, const std::list< Element* >::const_iterator& )
770 : {
771 : // xxx TODO copied from DrawElement
772 3 : const GraphicsContext& rGC = m_rProcessor.getGraphicsContext(elem.GCId );
773 3 : PropertyMap aProps;
774 3 : aProps[ "style:family" ] = "graphic";
775 3 : aProps[ "style:parent-style-name" ] = "standard";
776 : // generate standard graphic style if necessary
777 3 : m_rStyleContainer.getStandardStyleId( "graphic" );
778 :
779 6 : PropertyMap aGCProps;
780 :
781 : // TODO(F3): proper dash emulation
782 3 : if( elem.Action & PATH_STROKE )
783 : {
784 2 : aGCProps[ "draw:stroke" ] = rGC.DashArray.empty() ? OUString("solid") : OUString("dash");
785 2 : aGCProps[ "svg:stroke-color" ] = getColorString( rGC.LineColor );
786 2 : if( rGC.LineWidth != 0.0 )
787 : {
788 1 : ::basegfx::B2DVector aVec(rGC.LineWidth,0);
789 1 : aVec *= rGC.Transformation;
790 :
791 1 : aVec.setX ( convPx2mmPrec2( aVec.getX() )*100.0 );
792 1 : aVec.setY ( convPx2mmPrec2( aVec.getY() )*100.0 );
793 :
794 1 : aGCProps[ "svg:stroke-width" ] = OUString::valueOf( aVec.getLength() );
795 : }
796 : }
797 : else
798 : {
799 1 : aGCProps[ "draw:stroke" ] = "none";
800 : }
801 :
802 : // TODO(F1): check whether stuff could be emulated by gradient/bitmap/hatch
803 3 : if( elem.Action & (PATH_FILL | PATH_EOFILL) )
804 : {
805 1 : aGCProps[ "draw:fill" ] = "solid";
806 1 : aGCProps[ "draw:fill-color" ] = getColorString( rGC.FillColor );
807 : }
808 : else
809 : {
810 2 : aGCProps[ "draw:fill" ] = "none";
811 : }
812 :
813 6 : StyleContainer::Style aStyle( "style:style", aProps );
814 6 : StyleContainer::Style aSubStyle( "style:graphic-properties", aGCProps );
815 3 : aStyle.SubStyles.push_back( &aSubStyle );
816 :
817 6 : elem.StyleId = m_rStyleContainer.getStyleId( aStyle );
818 3 : }
819 :
820 0 : void DrawXmlFinalizer::visit( HyperlinkElement&, const std::list< Element* >::const_iterator& )
821 : {
822 0 : }
823 :
824 11 : void DrawXmlFinalizer::visit( TextElement& elem, const std::list< Element* >::const_iterator& )
825 : {
826 11 : const FontAttributes& rFont = m_rProcessor.getFont( elem.FontId );
827 11 : PropertyMap aProps;
828 11 : aProps[ "style:family" ] = "text";
829 :
830 22 : PropertyMap aFontProps;
831 :
832 : // family name
833 11 : aFontProps[ "fo:font-family" ] = rFont.familyName;
834 11 : aFontProps[ "style:font-family-complex" ] = rFont.familyName;
835 :
836 : // bold
837 11 : if( rFont.isBold )
838 : {
839 0 : aFontProps[ "fo:font-weight" ] = "bold";
840 0 : aFontProps[ "fo:font-weight-asian" ] = "bold";
841 0 : aFontProps[ "style:font-weight-complex" ] = "bold";
842 : }
843 : // italic
844 11 : if( rFont.isItalic )
845 : {
846 0 : aFontProps[ "fo:font-style" ] = "italic";
847 0 : aFontProps[ "fo:font-style-asian" ] = "italic";
848 0 : aFontProps[ "style:font-style-complex" ] = "italic";
849 : }
850 : // underline
851 11 : if( rFont.isUnderline )
852 : {
853 0 : aFontProps[ "style:text-underline-style" ] = "solid";
854 0 : aFontProps[ "style:text-underline-width" ] = "auto";
855 0 : aFontProps[ "style:text-underline-color" ] = "font-color";
856 : }
857 : // outline
858 11 : if( rFont.isOutline )
859 : {
860 0 : aFontProps[ "style:text-outline" ] = "true";
861 : }
862 : // size
863 22 : OUStringBuffer aBuf( 32 );
864 11 : aBuf.append( rFont.size*72/PDFI_OUTDEV_RESOLUTION );
865 11 : aBuf.appendAscii( "pt" );
866 22 : OUString aFSize = aBuf.makeStringAndClear();
867 11 : aFontProps[ "fo:font-size" ] = aFSize;
868 11 : aFontProps[ "style:font-size-asian" ] = aFSize;
869 11 : aFontProps[ "style:font-size-complex" ] = aFSize;
870 : // color
871 11 : const GraphicsContext& rGC = m_rProcessor.getGraphicsContext( elem.GCId );
872 11 : aFontProps[ "fo:color" ] = getColorString( rFont.isOutline ? rGC.LineColor : rGC.FillColor );
873 :
874 22 : StyleContainer::Style aStyle( "style:style", aProps );
875 22 : StyleContainer::Style aSubStyle( "style:text-properties", aFontProps );
876 11 : aStyle.SubStyles.push_back( &aSubStyle );
877 22 : elem.StyleId = m_rStyleContainer.getStyleId( aStyle );
878 11 : }
879 :
880 11 : void DrawXmlFinalizer::visit( ParagraphElement& elem, const std::list< Element* >::const_iterator& )
881 : {
882 :
883 11 : PropertyMap aProps;
884 11 : aProps[ "style:family" ] = "paragraph";
885 : // generate standard paragraph style if necessary
886 11 : m_rStyleContainer.getStandardStyleId( "paragraph" );
887 :
888 22 : PropertyMap aParProps;
889 :
890 11 : aParProps[ "fo:text-align"] = "start";
891 11 : if (elem.bRtl)
892 0 : aParProps[ "style:writing-mode"] = "rl-tb";
893 : else
894 11 : aParProps[ "style:writing-mode"] = "lr-tb";
895 :
896 22 : StyleContainer::Style aStyle( "style:style", aProps );
897 22 : StyleContainer::Style aSubStyle( "style:paragraph-properties", aParProps );
898 11 : aStyle.SubStyles.push_back( &aSubStyle );
899 :
900 11 : elem.StyleId = m_rStyleContainer.getStyleId( aStyle );
901 :
902 22 : elem.applyToChildren(*this);
903 11 : }
904 :
905 12 : void DrawXmlFinalizer::visit( FrameElement& elem, const std::list< Element* >::const_iterator&)
906 : {
907 12 : PropertyMap aProps;
908 12 : aProps[ "style:family" ] = "graphic";
909 12 : aProps[ "style:parent-style-name" ] = "standard";
910 : // generate standard graphic style if necessary
911 12 : m_rStyleContainer.getStandardStyleId( "graphic" );
912 :
913 24 : PropertyMap aGCProps;
914 :
915 12 : aGCProps[ "draw:stroke" ] = "none";
916 12 : aGCProps[ "draw:fill" ] = "none";
917 12 : aGCProps[ "draw:auto-grow-height" ] = "true";
918 12 : aGCProps[ "draw:auto-grow-width" ] = "true";
919 12 : aGCProps[ "draw:textarea-horizontal-align" ] = "left";
920 12 : aGCProps[ "draw:textarea-vertical-align" ] = "top";
921 12 : aGCProps[ "fo:min-height"] = "0cm";
922 12 : aGCProps[ "fo:min-width"] = "0cm";
923 12 : aGCProps[ "fo:padding-top" ] = "0cm";
924 12 : aGCProps[ "fo:padding-left" ] = "0cm";
925 12 : aGCProps[ "fo:padding-right" ] = "0cm";
926 12 : aGCProps[ "fo:padding-bottom" ] = "0cm";
927 :
928 : // remark: vertical mirroring is done in current OOO by
929 : // mirroring horzontally and rotating 180 degrees
930 : // this is quaint, but unfortunately it seems
931 : // mirror=vertical is defined but not implemented in current code
932 12 : if( elem.MirrorVertical )
933 0 : aGCProps[ "style:mirror" ] = "horizontal";
934 :
935 24 : StyleContainer::Style aStyle( "style:style", aProps );
936 24 : StyleContainer::Style aSubStyle( "style:graphic-properties", aGCProps );
937 12 : aStyle.SubStyles.push_back( &aSubStyle );
938 :
939 12 : elem.StyleId = m_rStyleContainer.getStyleId( aStyle );
940 24 : elem.applyToChildren(*this);
941 12 : }
942 :
943 1 : void DrawXmlFinalizer::visit( ImageElement&, const std::list< Element* >::const_iterator& )
944 : {
945 1 : }
946 :
947 1 : void DrawXmlFinalizer::visit( PageElement& elem, const std::list< Element* >::const_iterator& )
948 : {
949 1 : if( m_rProcessor.getStatusIndicator().is() )
950 0 : m_rProcessor.getStatusIndicator()->setValue( elem.PageNumber );
951 :
952 : // transform from pixel to mm
953 1 : double page_width = convPx2mm( elem.w ), page_height = convPx2mm( elem.h );
954 :
955 : // calculate page margins out of the relevant children (paragraphs)
956 1 : elem.TopMargin = elem.h, elem.BottomMargin = 0, elem.LeftMargin = elem.w, elem.RightMargin = 0;
957 :
958 16 : for( std::list< Element* >::const_iterator it = elem.Children.begin(); it != elem.Children.end(); ++it )
959 : {
960 15 : if( (*it)->x < elem.LeftMargin )
961 1 : elem.LeftMargin = (*it)->x;
962 15 : if( (*it)->y < elem.TopMargin )
963 2 : elem.TopMargin = (*it)->y;
964 15 : if( (*it)->x + (*it)->w > elem.RightMargin )
965 4 : elem.RightMargin = ((*it)->x + (*it)->w);
966 15 : if( (*it)->y + (*it)->h > elem.BottomMargin )
967 8 : elem.BottomMargin = ((*it)->y + (*it)->h);
968 : }
969 :
970 : // transform margins to mm
971 1 : double left_margin = convPx2mm( elem.LeftMargin );
972 1 : double right_margin = convPx2mm( elem.RightMargin );
973 1 : double top_margin = convPx2mm( elem.TopMargin );
974 1 : double bottom_margin = convPx2mm( elem.BottomMargin );
975 :
976 : // round left/top margin to nearest mm
977 1 : left_margin = rtl_math_round( left_margin, 0, rtl_math_RoundingMode_Floor );
978 1 : top_margin = rtl_math_round( top_margin, 0, rtl_math_RoundingMode_Floor );
979 : // round (fuzzy) right/bottom margin to nearest cm
980 1 : right_margin = rtl_math_round( right_margin, right_margin >= 10 ? -1 : 0, rtl_math_RoundingMode_Floor );
981 1 : bottom_margin = rtl_math_round( bottom_margin, bottom_margin >= 10 ? -1 : 0, rtl_math_RoundingMode_Floor );
982 :
983 : // set reasonable default in case of way too large margins
984 : // e.g. no paragraph case
985 1 : if( left_margin > page_width/2.0 - 10 )
986 0 : left_margin = 10;
987 1 : if( right_margin > page_width/2.0 - 10 )
988 1 : right_margin = 10;
989 1 : if( top_margin > page_height/2.0 - 10 )
990 0 : top_margin = 10;
991 1 : if( bottom_margin > page_height/2.0 - 10 )
992 1 : bottom_margin = 10;
993 :
994 : // catch the weird cases
995 1 : if( left_margin < 0 )
996 0 : left_margin = 0;
997 1 : if( right_margin < 0 )
998 0 : right_margin = 0;
999 1 : if( top_margin < 0 )
1000 0 : top_margin = 0;
1001 1 : if( bottom_margin < 0 )
1002 0 : bottom_margin = 0;
1003 :
1004 : // widely differing margins are unlikely to be correct
1005 1 : if( right_margin > left_margin*1.5 )
1006 0 : right_margin = left_margin;
1007 :
1008 1 : elem.LeftMargin = convmm2Px( left_margin );
1009 1 : elem.RightMargin = convmm2Px( right_margin );
1010 1 : elem.TopMargin = convmm2Px( top_margin );
1011 1 : elem.BottomMargin = convmm2Px( bottom_margin );
1012 :
1013 : // get styles for paragraphs
1014 1 : PropertyMap aPageProps;
1015 2 : PropertyMap aPageLayoutProps;
1016 1 : aPageLayoutProps[ "fo:margin-top" ] = unitMMString( top_margin );
1017 1 : aPageLayoutProps[ "fo:margin-bottom" ] = unitMMString( bottom_margin );
1018 1 : aPageLayoutProps[ "fo:margin-left" ] = unitMMString( left_margin );
1019 1 : aPageLayoutProps[ "fo:margin-right" ] = unitMMString( right_margin );
1020 1 : aPageLayoutProps[ "fo:page-width" ] = unitMMString( page_width );
1021 1 : aPageLayoutProps[ "fo:page-height" ] = unitMMString( page_height );
1022 1 : aPageLayoutProps[ "style:print-orientation" ]= elem.w < elem.h ? OUString("portrait") : OUString("landscape");
1023 1 : aPageLayoutProps[ "style:writing-mode" ]= "lr-tb";
1024 :
1025 2 : StyleContainer::Style aStyle( "style:page-layout", aPageProps);
1026 2 : StyleContainer::Style aSubStyle( "style:page-layout-properties", aPageLayoutProps);
1027 1 : aStyle.SubStyles.push_back(&aSubStyle);
1028 1 : sal_Int32 nPageStyle = m_rStyleContainer.impl_getStyleId( aStyle, false );
1029 :
1030 : // create master page
1031 2 : OUString aMasterPageLayoutName = m_rStyleContainer.getStyleName( nPageStyle );
1032 1 : aPageProps[ "style:page-layout-name" ] = aMasterPageLayoutName;
1033 :
1034 2 : StyleContainer::Style aMPStyle( "style:master-page", aPageProps);
1035 :
1036 2 : StyleContainer::Style aHeaderStyle( "style:header", PropertyMap() );
1037 2 : StyleContainer::Style aFooterStyle( "style:footer", PropertyMap() );
1038 :
1039 1 : elem.StyleId = m_rStyleContainer.impl_getStyleId( aMPStyle,false );
1040 :
1041 : // create styles for children
1042 2 : elem.applyToChildren(*this);
1043 1 : }
1044 :
1045 1 : void DrawXmlFinalizer::visit( DocumentElement& elem, const std::list< Element* >::const_iterator& )
1046 : {
1047 1 : elem.applyToChildren(*this);
1048 1 : }
1049 :
1050 : }
1051 :
1052 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|