1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_sdext.hxx" 30 31 #include "pdfiprocessor.hxx" 32 #include "xmlemitter.hxx" 33 #include "pdfihelper.hxx" 34 #include "imagecontainer.hxx" 35 #include "style.hxx" 36 #include "writertreevisiting.hxx" 37 #include "genericelements.hxx" 38 39 #include <basegfx/polygon/b2dpolypolygontools.hxx> 40 #include <basegfx/range/b2drange.hxx> 41 42 43 namespace pdfi 44 { 45 46 void WriterXmlEmitter::visit( HyperlinkElement& elem, const std::list< Element* >::const_iterator& ) 47 { 48 if( elem.Children.empty() ) 49 return; 50 51 const char* pType = dynamic_cast<DrawElement*>(elem.Children.front()) ? "draw:a" : "text:a"; 52 53 PropertyMap aProps; 54 aProps[ USTR( "xlink:type" ) ] = USTR( "simple" ); 55 aProps[ USTR( "xlink:href" ) ] = elem.URI; 56 aProps[ USTR( "office:target-frame-name" ) ] = USTR( "_blank" ); 57 aProps[ USTR( "xlink:show" ) ] = USTR( "new" ); 58 59 m_rEmitContext.rEmitter.beginTag( pType, aProps ); 60 std::list< Element* >::iterator this_it = elem.Children.begin(); 61 while( this_it !=elem.Children.end() && *this_it != &elem ) 62 { 63 (*this_it)->visitedBy( *this, this_it ); 64 this_it++; 65 } 66 m_rEmitContext.rEmitter.endTag( pType ); 67 } 68 69 void WriterXmlEmitter::visit( TextElement& elem, const std::list< Element* >::const_iterator& ) 70 { 71 if( ! elem.Text.getLength() ) 72 return; 73 74 PropertyMap aProps; 75 if( elem.StyleId != -1 ) 76 { 77 aProps[ rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "text:style-name" ) ) ] = 78 m_rEmitContext.rStyles.getStyleName( elem.StyleId ); 79 } 80 81 m_rEmitContext.rEmitter.beginTag( "text:span", aProps ); 82 m_rEmitContext.rEmitter.write( elem.Text.makeStringAndClear() ); 83 std::list< Element* >::iterator this_it = elem.Children.begin(); 84 while( this_it !=elem.Children.end() && *this_it != &elem ) 85 { 86 (*this_it)->visitedBy( *this, this_it ); 87 this_it++; 88 } 89 90 m_rEmitContext.rEmitter.endTag( "text:span" ); 91 } 92 93 void WriterXmlEmitter::visit( ParagraphElement& elem, const std::list< Element* >::const_iterator& ) 94 { 95 PropertyMap aProps; 96 if( elem.StyleId != -1 ) 97 { 98 aProps[ USTR( "text:style-name" ) ] = m_rEmitContext.rStyles.getStyleName( elem.StyleId ); 99 } 100 const char* pTagType = "text:p"; 101 if( elem.Type == elem.Headline ) 102 pTagType = "text:h"; 103 m_rEmitContext.rEmitter.beginTag( pTagType, aProps ); 104 105 std::list< Element* >::iterator this_it = elem.Children.begin(); 106 while( this_it !=elem.Children.end() && *this_it != &elem ) 107 { 108 (*this_it)->visitedBy( *this, this_it ); 109 this_it++; 110 } 111 112 m_rEmitContext.rEmitter.endTag( pTagType ); 113 } 114 115 void WriterXmlEmitter::fillFrameProps( DrawElement& rElem, 116 PropertyMap& rProps, 117 const EmitContext& rEmitContext ) 118 { 119 double rel_x = rElem.x, rel_y = rElem.y; 120 121 // find anchor type by recursing though parents 122 Element* pAnchor = rElem.Parent; 123 while( pAnchor && 124 ! dynamic_cast<ParagraphElement*>(pAnchor) && 125 ! dynamic_cast<PageElement*>(pAnchor) ) 126 { 127 pAnchor = pAnchor->Parent; 128 } 129 if( pAnchor ) 130 { 131 if( dynamic_cast<ParagraphElement*>(pAnchor) ) 132 { 133 rProps[ USTR( "text:anchor-type" ) ] = 134 rElem.isCharacter ? USTR( "character" ) : USTR( "paragraph" ); 135 } 136 else 137 { 138 PageElement* pPage = dynamic_cast<PageElement*>(pAnchor); 139 rProps[ USTR( "text:anchor-type" ) ] = USTR( "page" ); 140 rProps[ USTR( "text:anchor-page-number" ) ] = rtl::OUString::valueOf(pPage->PageNumber); 141 } 142 rel_x -= pAnchor->x; 143 rel_y -= pAnchor->y; 144 } 145 146 rProps[ USTR( "draw:z-index" ) ] = rtl::OUString::valueOf( rElem.ZOrder ); 147 rProps[ USTR( "draw:style-name" )] = rEmitContext.rStyles.getStyleName( rElem.StyleId ); 148 rProps[ USTR( "svg:width" ) ] = convertPixelToUnitString( rElem.w ); 149 rProps[ USTR( "svg:height" ) ] = convertPixelToUnitString( rElem.h ); 150 151 const GraphicsContext& rGC = 152 rEmitContext.rProcessor.getGraphicsContext( rElem.GCId ); 153 if( rGC.Transformation.isIdentity() ) 154 { 155 if( !rElem.isCharacter ) 156 { 157 rProps[ USTR( "svg:x" ) ] = convertPixelToUnitString( rel_x ); 158 rProps[ USTR( "svg:y" ) ] = convertPixelToUnitString( rel_y ); 159 } 160 } 161 else 162 { 163 basegfx::B2DTuple aScale, aTranslation; 164 double fRotate, fShearX; 165 166 rGC.Transformation.decompose( aScale, aTranslation, fRotate, fShearX ); 167 168 rtl::OUStringBuffer aBuf( 256 ); 169 170 // TODO(F2): general transformation case missing; if implemented, note 171 // that ODF rotation is oriented the other way 172 173 // build transformation string 174 if( fShearX != 0.0 ) 175 { 176 aBuf.appendAscii( "skewX( " ); 177 aBuf.append( fShearX ); 178 aBuf.appendAscii( " )" ); 179 } 180 if( fRotate != 0.0 ) 181 { 182 if( aBuf.getLength() > 0 ) 183 aBuf.append( sal_Unicode(' ') ); 184 aBuf.appendAscii( "rotate( " ); 185 aBuf.append( -fRotate ); 186 aBuf.appendAscii( " )" ); 187 188 } 189 if( ! rElem.isCharacter ) 190 { 191 if( aBuf.getLength() > 0 ) 192 aBuf.append( sal_Unicode(' ') ); 193 aBuf.appendAscii( "translate( " ); 194 aBuf.append( convertPixelToUnitString( rel_x ) ); 195 aBuf.append( sal_Unicode(' ') ); 196 aBuf.append( convertPixelToUnitString( rel_y ) ); 197 aBuf.appendAscii( " )" ); 198 } 199 200 rProps[ USTR( "draw:transform" ) ] = aBuf.makeStringAndClear(); 201 } 202 } 203 204 void WriterXmlEmitter::visit( FrameElement& elem, const std::list< Element* >::const_iterator& ) 205 { 206 if( elem.Children.empty() ) 207 return; 208 209 bool bTextBox = (dynamic_cast<ParagraphElement*>(elem.Children.front()) != NULL); 210 PropertyMap aFrameProps; 211 fillFrameProps( elem, aFrameProps, m_rEmitContext ); 212 m_rEmitContext.rEmitter.beginTag( "draw:frame", aFrameProps ); 213 if( bTextBox ) 214 m_rEmitContext.rEmitter.beginTag( "draw:text-box", PropertyMap() ); 215 216 std::list< Element* >::iterator this_it = elem.Children.begin(); 217 while( this_it !=elem.Children.end() && *this_it != &elem ) 218 { 219 (*this_it)->visitedBy( *this, this_it ); 220 this_it++; 221 } 222 223 if( bTextBox ) 224 m_rEmitContext.rEmitter.endTag( "draw:text-box" ); 225 m_rEmitContext.rEmitter.endTag( "draw:frame" ); 226 } 227 228 void WriterXmlEmitter::visit( PolyPolyElement& elem, const std::list< Element* >::const_iterator& ) 229 { 230 elem.updateGeometry(); 231 /* note: 232 * aw recommends using 100dth of mm in all respects since the xml import 233 * (a) is buggy (see issue 37213) 234 * (b) is optimized for 100dth of mm and does not scale itself then, 235 * this does not gain us speed but makes for smaller rounding errors since 236 * the xml importer coordinates are integer based 237 */ 238 for (sal_uInt32 i = 0; i< elem.PolyPoly.count(); i++) 239 { 240 basegfx::B2DPolygon b2dPolygon; 241 b2dPolygon = elem.PolyPoly.getB2DPolygon( i ); 242 243 for ( sal_uInt32 j = 0; j< b2dPolygon.count(); j++ ) 244 { 245 basegfx::B2DPoint point; 246 basegfx::B2DPoint nextPoint; 247 point = b2dPolygon.getB2DPoint( j ); 248 249 basegfx::B2DPoint prevPoint; 250 prevPoint = b2dPolygon.getPrevControlPoint( j ) ; 251 252 point.setX( convPx2mmPrec2( point.getX() )*100.0 ); 253 point.setY( convPx2mmPrec2( point.getY() )*100.0 ); 254 255 if ( b2dPolygon.isPrevControlPointUsed( j ) ) 256 { 257 prevPoint.setX( convPx2mmPrec2( prevPoint.getX() )*100.0 ); 258 prevPoint.setY( convPx2mmPrec2( prevPoint.getY() )*100.0 ); 259 } 260 261 if ( b2dPolygon.isNextControlPointUsed( j ) ) 262 { 263 nextPoint = b2dPolygon.getNextControlPoint( j ) ; 264 nextPoint.setX( convPx2mmPrec2( nextPoint.getX() )*100.0 ); 265 nextPoint.setY( convPx2mmPrec2( nextPoint.getY() )*100.0 ); 266 } 267 268 b2dPolygon.setB2DPoint( j, point ); 269 270 if ( b2dPolygon.isPrevControlPointUsed( j ) ) 271 b2dPolygon.setPrevControlPoint( j , prevPoint ) ; 272 273 if ( b2dPolygon.isNextControlPointUsed( j ) ) 274 b2dPolygon.setNextControlPoint( j , nextPoint ) ; 275 } 276 277 elem.PolyPoly.setB2DPolygon( i, b2dPolygon ); 278 } 279 280 PropertyMap aProps; 281 fillFrameProps( elem, aProps, m_rEmitContext ); 282 rtl::OUStringBuffer aBuf( 64 ); 283 aBuf.appendAscii( "0 0 " ); 284 aBuf.append( convPx2mmPrec2(elem.w)*100.0 ); 285 aBuf.append( sal_Unicode(' ') ); 286 aBuf.append( convPx2mmPrec2(elem.h)*100.0 ); 287 aProps[ USTR( "svg:viewBox" ) ] = aBuf.makeStringAndClear(); 288 aProps[ USTR( "svg:d" ) ] = basegfx::tools::exportToSvgD( elem.PolyPoly ); 289 290 m_rEmitContext.rEmitter.beginTag( "draw:path", aProps ); 291 m_rEmitContext.rEmitter.endTag( "draw:path" ); 292 } 293 294 void WriterXmlEmitter::visit( ImageElement& elem, const std::list< Element* >::const_iterator& ) 295 { 296 PropertyMap aImageProps; 297 m_rEmitContext.rEmitter.beginTag( "draw:image", aImageProps ); 298 m_rEmitContext.rEmitter.beginTag( "office:binary-data", PropertyMap() ); 299 m_rEmitContext.rImages.writeBase64EncodedStream( elem.Image, m_rEmitContext); 300 m_rEmitContext.rEmitter.endTag( "office:binary-data" ); 301 m_rEmitContext.rEmitter.endTag( "draw:image" ); 302 } 303 304 void WriterXmlEmitter::visit( PageElement& elem, const std::list< Element* >::const_iterator& ) 305 { 306 if( m_rEmitContext.xStatusIndicator.is() ) 307 m_rEmitContext.xStatusIndicator->setValue( elem.PageNumber ); 308 309 std::list< Element* >::iterator this_it = elem.Children.begin(); 310 while( this_it !=elem.Children.end() && *this_it != &elem ) 311 { 312 (*this_it)->visitedBy( *this, this_it ); 313 this_it++; 314 } 315 } 316 317 void WriterXmlEmitter::visit( DocumentElement& elem, const std::list< Element* >::const_iterator&) 318 { 319 m_rEmitContext.rEmitter.beginTag( "office:body", PropertyMap() ); 320 m_rEmitContext.rEmitter.beginTag( "office:text", PropertyMap() ); 321 322 for( std::list< Element* >::iterator it = elem.Children.begin(); it != elem.Children.end(); ++it ) 323 { 324 PageElement* pPage = dynamic_cast<PageElement*>(*it); 325 if( pPage ) 326 { 327 // emit only page anchored objects 328 // currently these are only DrawElement types 329 for( std::list< Element* >::iterator child_it = pPage->Children.begin(); child_it != pPage->Children.end(); ++child_it ) 330 { 331 if( dynamic_cast<DrawElement*>(*child_it) != NULL ) 332 (*child_it)->visitedBy( *this, child_it ); 333 } 334 } 335 } 336 337 // do not emit page anchored objects, they are emitted before 338 // (must precede all pages in writer document) currently these are 339 // only DrawElement types 340 for( std::list< Element* >::iterator it = elem.Children.begin(); it != elem.Children.end(); ++it ) 341 { 342 if( dynamic_cast<DrawElement*>(*it) == NULL ) 343 (*it)->visitedBy( *this, it ); 344 } 345 346 m_rEmitContext.rEmitter.endTag( "office:text" ); 347 m_rEmitContext.rEmitter.endTag( "office:body" ); 348 } 349 350 ///////////////////////////////////////////////////////////////// 351 352 void WriterXmlOptimizer::visit( HyperlinkElement&, const std::list< Element* >::const_iterator& ) 353 { 354 } 355 356 void WriterXmlOptimizer::visit( TextElement&, const std::list< Element* >::const_iterator&) 357 { 358 } 359 360 void WriterXmlOptimizer::visit( FrameElement& elem, const std::list< Element* >::const_iterator& ) 361 { 362 elem.applyToChildren(*this); 363 } 364 365 void WriterXmlOptimizer::visit( ImageElement&, const std::list< Element* >::const_iterator& ) 366 { 367 } 368 369 void WriterXmlOptimizer::visit( PolyPolyElement& elem, const std::list< Element* >::const_iterator& ) 370 { 371 /* note: optimize two consecutive PolyPolyElements that 372 * have the same path but one of which is a stroke while 373 * the other is a fill 374 */ 375 if( elem.Parent ) 376 { 377 // find following PolyPolyElement in parent's children list 378 std::list< Element* >::iterator this_it = elem.Parent->Children.begin(); 379 while( this_it != elem.Parent->Children.end() && *this_it != &elem ) 380 ++this_it; 381 382 if( this_it != elem.Parent->Children.end() ) 383 { 384 std::list< Element* >::iterator next_it = this_it; 385 if( ++next_it != elem.Parent->Children.end() ) 386 { 387 PolyPolyElement* pNext = dynamic_cast<PolyPolyElement*>(*next_it); 388 if( pNext && pNext->PolyPoly == elem.PolyPoly ) 389 { 390 const GraphicsContext& rNextGC = 391 m_rProcessor.getGraphicsContext( pNext->GCId ); 392 const GraphicsContext& rThisGC = 393 m_rProcessor.getGraphicsContext( elem.GCId ); 394 395 if( rThisGC.BlendMode == rNextGC.BlendMode && 396 rThisGC.Flatness == rNextGC.Flatness && 397 rThisGC.Transformation == rNextGC.Transformation && 398 rThisGC.Clip == rNextGC.Clip && 399 pNext->Action == PATH_STROKE && 400 (elem.Action == PATH_FILL || elem.Action == PATH_EOFILL) ) 401 { 402 GraphicsContext aGC = rThisGC; 403 aGC.LineJoin = rNextGC.LineJoin; 404 aGC.LineCap = rNextGC.LineCap; 405 aGC.LineWidth = rNextGC.LineWidth; 406 aGC.MiterLimit= rNextGC.MiterLimit; 407 aGC.DashArray = rNextGC.DashArray; 408 aGC.LineColor = rNextGC.LineColor; 409 elem.GCId = m_rProcessor.getGCId( aGC ); 410 411 elem.Action |= pNext->Action; 412 413 elem.Children.splice( elem.Children.end(), pNext->Children ); 414 elem.Parent->Children.erase( next_it ); 415 delete pNext; 416 } 417 } 418 } 419 } 420 } 421 } 422 423 void WriterXmlOptimizer::visit( ParagraphElement& elem, const std::list< Element* >::const_iterator& rParentIt) 424 { 425 optimizeTextElements( elem ); 426 427 elem.applyToChildren(*this); 428 429 if( elem.Parent && rParentIt != elem.Parent->Children.end() ) 430 { 431 // find if there is a previous paragraph that might be a heading for this one 432 std::list<Element*>::const_iterator prev = rParentIt; 433 ParagraphElement* pPrevPara = NULL; 434 while( prev != elem.Parent->Children.begin() ) 435 { 436 --prev; 437 pPrevPara = dynamic_cast< ParagraphElement* >(*prev); 438 if( pPrevPara ) 439 { 440 /* What constitutes a heading ? current hints are: 441 * - one line only 442 * - not too far away from this paragraph (two heading height max ?) 443 * - font larger or bold 444 * this is of course incomplete 445 * FIXME: improve hints for heading 446 */ 447 // check for single line 448 if( pPrevPara->isSingleLined( m_rProcessor ) ) 449 { 450 double head_line_height = pPrevPara->getLineHeight( m_rProcessor ); 451 if( pPrevPara->y + pPrevPara->h + 2*head_line_height > elem.y ) 452 { 453 // check for larger font 454 if( head_line_height > elem.getLineHeight( m_rProcessor ) ) 455 { 456 pPrevPara->Type = elem.Headline; 457 } 458 else 459 { 460 // check whether text of pPrevPara is bold (at least first text element) 461 // and this para is not bold (dito) 462 TextElement* pPrevText = pPrevPara->getFirstTextChild(); 463 TextElement* pThisText = elem.getFirstTextChild(); 464 if( pPrevText && pThisText ) 465 { 466 const FontAttributes& rPrevFont = m_rProcessor.getFont( pPrevText->FontId ); 467 const FontAttributes& rThisFont = m_rProcessor.getFont( pThisText->FontId ); 468 if( rPrevFont.isBold && ! rThisFont.isBold ) 469 pPrevPara->Type = elem.Headline; 470 } 471 } 472 } 473 } 474 break; 475 } 476 } 477 } 478 } 479 480 void WriterXmlOptimizer::visit( PageElement& elem, const std::list< Element* >::const_iterator& ) 481 { 482 if( m_rProcessor.getStatusIndicator().is() ) 483 m_rProcessor.getStatusIndicator()->setValue( elem.PageNumber ); 484 485 // resolve hyperlinks 486 elem.resolveHyperlinks(); 487 488 elem.resolveFontStyles( m_rProcessor ); // underlines and such 489 490 // FIXME: until hyperlinks and font effects are adjusted for 491 // geometrical search handle them before sorting 492 m_rProcessor.sortElements( &elem ); 493 494 // find paragraphs in text 495 ParagraphElement* pCurPara = NULL; 496 std::list< Element* >::iterator page_element, next_page_element; 497 next_page_element = elem.Children.begin(); 498 double fCurLineHeight = 0.0; // average height of text items in current para 499 int nCurLineElements = 0; // number of line contributing elements in current para 500 double line_left = elem.w, line_right = 0.0; 501 double column_width = elem.w*0.75; // estimate text width 502 // TODO: guess columns 503 while( next_page_element != elem.Children.end() ) 504 { 505 page_element = next_page_element++; 506 ParagraphElement* pPagePara = dynamic_cast<ParagraphElement*>(*page_element); 507 if( pPagePara ) 508 { 509 pCurPara = pPagePara; 510 // adjust line height and text items 511 fCurLineHeight = 0.0; 512 nCurLineElements = 0; 513 for( std::list< Element* >::iterator it = pCurPara->Children.begin(); 514 it != pCurPara->Children.end(); ++it ) 515 { 516 TextElement* pTestText = dynamic_cast<TextElement*>(*it); 517 if( pTestText ) 518 { 519 fCurLineHeight = (fCurLineHeight*double(nCurLineElements) + pTestText->h)/double(nCurLineElements+1); 520 nCurLineElements++; 521 } 522 } 523 continue; 524 } 525 526 HyperlinkElement* pLink = dynamic_cast<HyperlinkElement*>(*page_element); 527 DrawElement* pDraw = dynamic_cast<DrawElement*>(*page_element); 528 if( ! pDraw && pLink && ! pLink->Children.empty() ) 529 pDraw = dynamic_cast<DrawElement*>(pLink->Children.front() ); 530 if( pDraw ) 531 { 532 // insert small drawing objects as character, else leave them page bound 533 534 bool bInsertToParagraph = false; 535 // first check if this is either inside the paragraph 536 if( pCurPara && pDraw->y < pCurPara->y + pCurPara->h ) 537 { 538 if( pDraw->h < fCurLineHeight * 1.5 ) 539 { 540 bInsertToParagraph = true; 541 fCurLineHeight = (fCurLineHeight*double(nCurLineElements) + pDraw->h)/double(nCurLineElements+1); 542 nCurLineElements++; 543 // mark draw element as character 544 pDraw->isCharacter = true; 545 } 546 } 547 // or perhaps the draw element begins a new paragraph 548 else if( next_page_element != elem.Children.end() ) 549 { 550 TextElement* pText = dynamic_cast<TextElement*>(*next_page_element); 551 if( ! pText ) 552 { 553 ParagraphElement* pPara = dynamic_cast<ParagraphElement*>(*next_page_element); 554 if( pPara && ! pPara->Children.empty() ) 555 pText = dynamic_cast<TextElement*>(pPara->Children.front()); 556 } 557 if( pText && // check there is a text 558 pDraw->h < pText->h*1.5 && // and it is approx the same height 559 // and either upper or lower edge of pDraw is inside text's vertical range 560 ( ( pDraw->y >= pText->y && pDraw->y <= pText->y+pText->h ) || 561 ( pDraw->y+pDraw->h >= pText->y && pDraw->y+pDraw->h <= pText->y+pText->h ) 562 ) 563 ) 564 { 565 bInsertToParagraph = true; 566 fCurLineHeight = pDraw->h; 567 nCurLineElements = 1; 568 line_left = pDraw->x; 569 line_right = pDraw->x + pDraw->w; 570 // begin a new paragraph 571 pCurPara = NULL; 572 // mark draw element as character 573 pDraw->isCharacter = true; 574 } 575 } 576 577 if( ! bInsertToParagraph ) 578 { 579 pCurPara = NULL; 580 continue; 581 } 582 } 583 584 TextElement* pText = dynamic_cast<TextElement*>(*page_element); 585 if( ! pText && pLink && ! pLink->Children.empty() ) 586 pText = dynamic_cast<TextElement*>(pLink->Children.front()); 587 if( pText ) 588 { 589 Element* pGeo = pLink ? static_cast<Element*>(pLink) : 590 static_cast<Element*>(pText); 591 if( pCurPara ) 592 { 593 // there was already a text element, check for a new paragraph 594 if( nCurLineElements > 0 ) 595 { 596 // if the new text is significantly distant from the paragraph 597 // begin a new paragraph 598 if( pGeo->y > pCurPara->y+pCurPara->h + fCurLineHeight*0.5 ) 599 pCurPara = NULL; // insert new paragraph 600 else if( pGeo->y > (pCurPara->y+pCurPara->h - fCurLineHeight*0.05) ) 601 { 602 // new paragraph if either the last line of the paragraph 603 // was significantly shorter than the paragraph as a whole 604 if( (line_right - line_left) < pCurPara->w*0.75 ) 605 pCurPara = NULL; 606 // or the last line was significantly smaller than the column width 607 else if( (line_right - line_left) < column_width*0.75 ) 608 pCurPara = NULL; 609 } 610 } 611 } 612 // update line height/width 613 if( pCurPara ) 614 { 615 fCurLineHeight = (fCurLineHeight*double(nCurLineElements) + pGeo->h)/double(nCurLineElements+1); 616 nCurLineElements++; 617 if( pGeo->x < line_left ) 618 line_left = pGeo->x; 619 if( pGeo->x+pGeo->w > line_right ) 620 line_right = pGeo->x+pGeo->w; 621 } 622 else 623 { 624 fCurLineHeight = pGeo->h; 625 nCurLineElements = 1; 626 line_left = pGeo->x; 627 line_right = pGeo->x + pGeo->w; 628 } 629 } 630 631 // move element to current paragraph 632 if( ! pCurPara ) // new paragraph, insert one 633 { 634 pCurPara = m_rProcessor.getElementFactory()->createParagraphElement( NULL ); 635 // set parent 636 pCurPara->Parent = &elem; 637 //insert new paragraph before current element 638 page_element = elem.Children.insert( page_element, pCurPara ); 639 // forward iterator to current element again 640 ++ page_element; 641 // update next_element which is now invalid 642 next_page_element = page_element; 643 ++ next_page_element; 644 } 645 Element* pCurEle = *page_element; 646 pCurEle->setParent( page_element, pCurPara ); 647 OSL_ENSURE( !pText || pCurEle == pText || pCurEle == pLink, "paragraph child list in disorder" ); 648 if( pText || pDraw ) 649 pCurPara->updateGeometryWith( pCurEle ); 650 } 651 652 // process children 653 elem.applyToChildren(*this); 654 655 // find possible header and footer 656 checkHeaderAndFooter( elem ); 657 } 658 659 void WriterXmlOptimizer::checkHeaderAndFooter( PageElement& rElem ) 660 { 661 /* indicators for a header: 662 * - single line paragrah at top of page ( inside 15% page height) 663 * - at least linheight above the next paragr aph 664 * 665 * indicators for a footer likewise: 666 * - single line paragraph at bottom of page (inside 15% page height) 667 * - at least lineheight below the previous paragraph 668 */ 669 670 // detect header 671 // Note: the following assumes that the pages' chiuldren have been 672 // sorted geometrically 673 std::list< Element* >::iterator it = rElem.Children.begin(); 674 while( it != rElem.Children.end() ) 675 { 676 ParagraphElement* pPara = dynamic_cast<ParagraphElement*>(*it); 677 if( pPara ) 678 { 679 if( pPara->y+pPara->h < rElem.h*0.15 && pPara->isSingleLined( m_rProcessor ) ) 680 { 681 std::list< Element* >::iterator next_it = it; 682 ParagraphElement* pNextPara = NULL; 683 while( ++next_it != rElem.Children.end() && pNextPara == NULL ) 684 { 685 pNextPara = dynamic_cast<ParagraphElement*>(*next_it); 686 } 687 if( pNextPara && pNextPara->y > pPara->y+pPara->h*2 ) 688 { 689 rElem.HeaderElement = pPara; 690 pPara->Parent = NULL; 691 rElem.Children.remove( pPara ); 692 } 693 } 694 break; 695 } 696 ++it; 697 } 698 699 // detect footer 700 std::list< Element* >::reverse_iterator rit = rElem.Children.rbegin(); 701 while( rit != rElem.Children.rend() ) 702 { 703 ParagraphElement* pPara = dynamic_cast<ParagraphElement*>(*rit); 704 if( pPara ) 705 { 706 if( pPara->y > rElem.h*0.85 && pPara->isSingleLined( m_rProcessor ) ) 707 { 708 std::list< Element* >::reverse_iterator next_it = rit; 709 ParagraphElement* pNextPara = NULL; 710 while( ++next_it != rElem.Children.rend() && pNextPara == NULL ) 711 { 712 pNextPara = dynamic_cast<ParagraphElement*>(*next_it); 713 } 714 if( pNextPara && pNextPara->y < pPara->y-pPara->h*2 ) 715 { 716 rElem.FooterElement = pPara; 717 pPara->Parent = NULL; 718 rElem.Children.remove( pPara ); 719 } 720 } 721 break; 722 } 723 ++rit; 724 } 725 } 726 727 void WriterXmlOptimizer::optimizeTextElements(Element& rParent) 728 { 729 if( rParent.Children.empty() ) // this should not happen 730 { 731 OSL_ENSURE( 0, "empty paragraph optimized" ); 732 return; 733 } 734 735 // concatenate child elements with same font id 736 std::list< Element* >::iterator next = rParent.Children.begin(); 737 std::list< Element* >::iterator it = next++; 738 FrameElement* pFrame = dynamic_cast<FrameElement*>(rParent.Parent); 739 bool bRotatedFrame = false; 740 if( pFrame ) 741 { 742 const GraphicsContext& rFrameGC = m_rProcessor.getGraphicsContext( pFrame->GCId ); 743 if( rFrameGC.isRotatedOrSkewed() ) 744 bRotatedFrame = true; 745 } 746 while( next != rParent.Children.end() ) 747 { 748 bool bConcat = false; 749 TextElement* pCur = dynamic_cast<TextElement*>(*it); 750 if( pCur ) 751 { 752 TextElement* pNext = dynamic_cast<TextElement*>(*next); 753 if( pNext ) 754 { 755 const GraphicsContext& rCurGC = m_rProcessor.getGraphicsContext( pCur->GCId ); 756 const GraphicsContext& rNextGC = m_rProcessor.getGraphicsContext( pNext->GCId ); 757 758 // line and space optimization; works only in strictly horizontal mode 759 760 if( !bRotatedFrame 761 && ! rCurGC.isRotatedOrSkewed() 762 && ! rNextGC.isRotatedOrSkewed() 763 && pNext->Text.charAt( 0 ) != sal_Unicode(' ') 764 && pCur->Text.getLength() > 0 765 && pCur->Text.charAt( pCur->Text.getLength()-1 ) != sal_Unicode(' ') 766 ) 767 { 768 // check for new line in paragraph 769 if( pNext->y > pCur->y+pCur->h ) 770 { 771 // new line begins 772 // check whether a space would should be inserted or a hyphen removed 773 sal_Unicode aLastCode = pCur->Text.charAt( pCur->Text.getLength()-1 ); 774 if( aLastCode == '-' 775 || aLastCode == 0x2010 776 || (aLastCode >= 0x2012 && aLastCode <= 0x2015) 777 || aLastCode == 0xff0d 778 ) 779 { 780 // cut a hyphen 781 pCur->Text.setLength( pCur->Text.getLength()-1 ); 782 } 783 // append a space unless there is a non breaking hyphen 784 else if( aLastCode != 0x2011 ) 785 { 786 pCur->Text.append( sal_Unicode( ' ' ) ); 787 } 788 } 789 else // we're continuing the same line 790 { 791 // check whether a space would should be inserted 792 // check for a small horizontal offset 793 if( pCur->x + pCur->w + pNext->h*0.15 < pNext->x ) 794 { 795 pCur->Text.append( sal_Unicode(' ') ); 796 } 797 } 798 } 799 // concatenate consecutive text elements unless there is a 800 // font or text color or matrix change, leave a new span in that case 801 if( pCur->FontId == pNext->FontId && 802 rCurGC.FillColor.Red == rNextGC.FillColor.Red && 803 rCurGC.FillColor.Green == rNextGC.FillColor.Green && 804 rCurGC.FillColor.Blue == rNextGC.FillColor.Blue && 805 rCurGC.FillColor.Alpha == rNextGC.FillColor.Alpha && 806 rCurGC.Transformation == rNextGC.Transformation 807 ) 808 { 809 pCur->updateGeometryWith( pNext ); 810 // append text to current element 811 pCur->Text.append( pNext->Text.getStr(), pNext->Text.getLength() ); 812 // append eventual children to current element 813 // and clear children (else the children just 814 // appended to pCur would be destroyed) 815 pCur->Children.splice( pCur->Children.end(), pNext->Children ); 816 // get rid of the now useless element 817 rParent.Children.erase( next ); 818 delete pNext; 819 bConcat = true; 820 } 821 } 822 } 823 else if( dynamic_cast<HyperlinkElement*>(*it) ) 824 optimizeTextElements( **it ); 825 if( bConcat ) 826 { 827 next = it; 828 ++next; 829 } 830 else 831 { 832 ++it; 833 ++next; 834 } 835 } 836 } 837 838 void WriterXmlOptimizer::visit( DocumentElement& elem, const std::list< Element* >::const_iterator&) 839 { 840 elem.applyToChildren(*this); 841 } 842 843 ////////////////////////////////////////////////////////////////////////////////// 844 845 846 void WriterXmlFinalizer::visit( PolyPolyElement& elem, const std::list< Element* >::const_iterator& ) 847 { 848 // xxx TODO copied from DrawElement 849 const GraphicsContext& rGC = m_rProcessor.getGraphicsContext(elem.GCId ); 850 PropertyMap aProps; 851 aProps[ USTR( "style:family" ) ] = USTR( "graphic" ); 852 853 PropertyMap aGCProps; 854 855 // TODO(F3): proper dash emulation 856 if( elem.Action & PATH_STROKE ) 857 { 858 aGCProps[ USTR("draw:stroke") ] = rGC.DashArray.empty() ? USTR("solid") : USTR("dash"); 859 aGCProps[ USTR("svg:stroke-color") ] = getColorString( rGC.LineColor ); 860 if( rGC.LineWidth != 0.0 ) 861 { 862 ::basegfx::B2DVector aVec(rGC.LineWidth,0); 863 aVec *= rGC.Transformation; 864 865 aVec.setX ( convPx2mmPrec2( aVec.getX() )*100.0 ); 866 aVec.setY ( convPx2mmPrec2( aVec.getY() )*100.0 ); 867 868 aGCProps[ USTR("svg:stroke-width") ] = rtl::OUString::valueOf( aVec.getLength() ); 869 } 870 } 871 else 872 { 873 aGCProps[ USTR("draw:stroke") ] = USTR("none"); 874 } 875 876 // TODO(F1): check whether stuff could be emulated by gradient/bitmap/hatch 877 if( elem.Action & (PATH_FILL | PATH_EOFILL) ) 878 { 879 aGCProps[ USTR("draw:fill") ] = USTR("solid"); 880 aGCProps[ USTR("draw:fill-color") ] = getColorString( rGC.FillColor ); 881 } 882 else 883 { 884 aGCProps[ USTR("draw:fill") ] = USTR("none"); 885 } 886 887 StyleContainer::Style aStyle( "style:style", aProps ); 888 StyleContainer::Style aSubStyle( "style:graphic-properties", aGCProps ); 889 aStyle.SubStyles.push_back( &aSubStyle ); 890 891 elem.StyleId = m_rStyleContainer.getStyleId( aStyle ); 892 } 893 894 void WriterXmlFinalizer::visit( HyperlinkElement&, const std::list< Element* >::const_iterator& ) 895 { 896 } 897 898 void WriterXmlFinalizer::visit( TextElement& elem, const std::list< Element* >::const_iterator& ) 899 { 900 const FontAttributes& rFont = m_rProcessor.getFont( elem.FontId ); 901 PropertyMap aProps; 902 aProps[ USTR( "style:family" ) ] = USTR( "text" ); 903 904 PropertyMap aFontProps; 905 906 // family name 907 aFontProps[ USTR( "fo:font-family" ) ] = rFont.familyName; 908 // bold 909 if( rFont.isBold ) 910 { 911 aFontProps[ USTR( "fo:font-weight" ) ] = USTR( "bold" ); 912 aFontProps[ USTR( "fo:font-weight-asian" ) ] = USTR( "bold" ); 913 aFontProps[ USTR( "fo:font-weight-complex" ) ] = USTR( "bold" ); 914 } 915 // italic 916 if( rFont.isItalic ) 917 { 918 aFontProps[ USTR( "fo:font-style" ) ] = USTR( "italic" ); 919 aFontProps[ USTR( "fo:font-style-asian" ) ] = USTR( "italic" ); 920 aFontProps[ USTR( "fo:font-style-complex" ) ] = USTR( "italic" ); 921 } 922 // underline 923 if( rFont.isUnderline ) 924 { 925 aFontProps[ USTR( "style:text-underline-style" ) ] = USTR( "solid" ); 926 aFontProps[ USTR( "style:text-underline-width" ) ] = USTR( "auto" ); 927 aFontProps[ USTR( "style:text-underline-color" ) ] = USTR( "font-color" ); 928 } 929 // outline 930 if( rFont.isOutline ) 931 { 932 aFontProps[ USTR( "style:text-outline" ) ] = USTR( "true" ); 933 } 934 // size 935 rtl::OUStringBuffer aBuf( 32 ); 936 aBuf.append( rFont.size*72/PDFI_OUTDEV_RESOLUTION ); 937 aBuf.appendAscii( "pt" ); 938 rtl::OUString aFSize = aBuf.makeStringAndClear(); 939 aFontProps[ USTR( "fo:font-size" ) ] = aFSize; 940 aFontProps[ USTR( "style:font-size-asian" ) ] = aFSize; 941 aFontProps[ USTR( "style:font-size-complex" ) ] = aFSize; 942 // color 943 const GraphicsContext& rGC = m_rProcessor.getGraphicsContext( elem.GCId ); 944 aFontProps[ USTR( "fo:color" ) ] = getColorString( rFont.isOutline ? rGC.LineColor : rGC.FillColor ); 945 946 StyleContainer::Style aStyle( "style:style", aProps ); 947 StyleContainer::Style aSubStyle( "style:text-properties", aFontProps ); 948 aStyle.SubStyles.push_back( &aSubStyle ); 949 elem.StyleId = m_rStyleContainer.getStyleId( aStyle ); 950 } 951 952 void WriterXmlFinalizer::visit( ParagraphElement& elem, const std::list< Element* >::const_iterator& rParentIt ) 953 { 954 PropertyMap aParaProps; 955 956 if( elem.Parent ) 957 { 958 // check for center alignement 959 // criterion: paragraph is small relative to parent and distributed around its center 960 double p_x = elem.Parent->x; 961 double p_y = elem.Parent->y; 962 double p_w = elem.Parent->w; 963 double p_h = elem.Parent->h; 964 965 PageElement* pPage = dynamic_cast<PageElement*>(elem.Parent); 966 if( pPage ) 967 { 968 p_x += pPage->LeftMargin; 969 p_y += pPage->TopMargin; 970 p_w -= pPage->LeftMargin+pPage->RightMargin; 971 p_h -= pPage->TopMargin+pPage->BottomMargin; 972 } 973 bool bIsCenter = false; 974 if( elem.w < ( p_w/2) ) 975 { 976 double delta = elem.w/4; 977 // allow very small paragraphs to deviate a little more 978 // relative to parent's center 979 if( elem.w < p_w/8 ) 980 delta = elem.w; 981 if( fabs( elem.x+elem.w/2 - ( p_x+ p_w/2) ) < delta || 982 (pPage && fabs( elem.x+elem.w/2 - (pPage->x + pPage->w/2) ) < delta) ) 983 { 984 bIsCenter = true; 985 aParaProps[ USTR( "fo:text-align" ) ] = USTR( "center" ); 986 } 987 } 988 if( ! bIsCenter && elem.x > p_x + p_w/10 ) 989 { 990 // indent 991 rtl::OUStringBuffer aBuf( 32 ); 992 aBuf.append( convPx2mm( elem.x - p_x ) ); 993 aBuf.appendAscii( "mm" ); 994 aParaProps[ USTR( "fo:margin-left" ) ] = aBuf.makeStringAndClear(); 995 } 996 997 // check whether to leave some space to next paragraph 998 // find wether there is a next paragraph 999 std::list< Element* >::const_iterator it = rParentIt; 1000 const ParagraphElement* pNextPara = NULL; 1001 while( ++it != elem.Parent->Children.end() && ! pNextPara ) 1002 pNextPara = dynamic_cast< const ParagraphElement* >(*it); 1003 if( pNextPara ) 1004 { 1005 if( pNextPara->y - (elem.y+elem.h) > convmm2Px( 10 ) ) 1006 { 1007 rtl::OUStringBuffer aBuf( 32 ); 1008 aBuf.append( convPx2mm( pNextPara->y - (elem.y+elem.h) ) ); 1009 aBuf.appendAscii( "mm" ); 1010 aParaProps[ USTR( "fo:margin-bottom" ) ] = aBuf.makeStringAndClear(); 1011 } 1012 } 1013 } 1014 1015 if( ! aParaProps.empty() ) 1016 { 1017 PropertyMap aProps; 1018 aProps[ USTR( "style:family" ) ] = USTR( "paragraph" ); 1019 StyleContainer::Style aStyle( "style:style", aProps ); 1020 StyleContainer::Style aSubStyle( "style:paragraph-properties", aParaProps ); 1021 aStyle.SubStyles.push_back( &aSubStyle ); 1022 elem.StyleId = m_rStyleContainer.getStyleId( aStyle ); 1023 } 1024 1025 elem.applyToChildren(*this); 1026 } 1027 1028 void WriterXmlFinalizer::visit( FrameElement& elem, const std::list< Element* >::const_iterator&) 1029 { 1030 PropertyMap aProps; 1031 aProps[ USTR( "style:family" ) ] = USTR( "graphic" ); 1032 1033 PropertyMap aGCProps; 1034 1035 aGCProps[ USTR("draw:stroke") ] = USTR("none"); 1036 aGCProps[ USTR("draw:fill") ] = USTR("none"); 1037 1038 StyleContainer::Style aStyle( "style:style", aProps ); 1039 StyleContainer::Style aSubStyle( "style:graphic-properties", aGCProps ); 1040 aStyle.SubStyles.push_back( &aSubStyle ); 1041 1042 elem.StyleId = m_rStyleContainer.getStyleId( aStyle ); 1043 elem.applyToChildren(*this); 1044 } 1045 1046 void WriterXmlFinalizer::visit( ImageElement&, const std::list< Element* >::const_iterator& ) 1047 { 1048 } 1049 1050 void WriterXmlFinalizer::setFirstOnPage( ParagraphElement& rElem, 1051 StyleContainer& rStyles, 1052 const rtl::OUString& rMasterPageName ) 1053 { 1054 PropertyMap aProps; 1055 if( rElem.StyleId != -1 ) 1056 { 1057 const PropertyMap* pProps = rStyles.getProperties( rElem.StyleId ); 1058 if( pProps ) 1059 aProps = *pProps; 1060 } 1061 1062 aProps[ USTR( "style:family" ) ] = USTR( "paragraph" ); 1063 aProps[ USTR( "style:master-page-name" ) ] = rMasterPageName; 1064 1065 if( rElem.StyleId != -1 ) 1066 rElem.StyleId = rStyles.setProperties( rElem.StyleId, aProps ); 1067 else 1068 { 1069 StyleContainer::Style aStyle( "style:style", aProps ); 1070 rElem.StyleId = rStyles.getStyleId( aStyle ); 1071 } 1072 } 1073 1074 void WriterXmlFinalizer::visit( PageElement& elem, const std::list< Element* >::const_iterator& ) 1075 { 1076 if( m_rProcessor.getStatusIndicator().is() ) 1077 m_rProcessor.getStatusIndicator()->setValue( elem.PageNumber ); 1078 1079 // transform from pixel to mm 1080 double page_width = convPx2mm( elem.w ), page_height = convPx2mm( elem.h ); 1081 1082 // calculate page margins out of the relevant children (paragraphs) 1083 elem.TopMargin = elem.h, elem.BottomMargin = 0, elem.LeftMargin = elem.w, elem.RightMargin = 0; 1084 // first element should be a paragraphy 1085 ParagraphElement* pFirstPara = NULL; 1086 for( std::list< Element* >::const_iterator it = elem.Children.begin(); it != elem.Children.end(); ++it ) 1087 { 1088 if( dynamic_cast<ParagraphElement*>( *it ) ) 1089 { 1090 if( (*it)->x < elem.LeftMargin ) 1091 elem.LeftMargin = (*it)->x; 1092 if( (*it)->y < elem.TopMargin ) 1093 elem.TopMargin = (*it)->y; 1094 if( (*it)->x + (*it)->w > elem.w - elem.RightMargin ) 1095 elem.RightMargin = elem.w - ((*it)->x + (*it)->w); 1096 if( (*it)->y + (*it)->h > elem.h - elem.BottomMargin ) 1097 elem.BottomMargin = elem.h - ((*it)->y + (*it)->h); 1098 if( ! pFirstPara ) 1099 pFirstPara = dynamic_cast<ParagraphElement*>( *it ); 1100 } 1101 } 1102 if( elem.HeaderElement && elem.HeaderElement->y < elem.TopMargin ) 1103 elem.TopMargin = elem.HeaderElement->y; 1104 if( elem.FooterElement && elem.FooterElement->y+elem.FooterElement->h > elem.h - elem.BottomMargin ) 1105 elem.BottomMargin = elem.h - (elem.FooterElement->y + elem.FooterElement->h); 1106 1107 // transform margins to mm 1108 double left_margin = convPx2mm( elem.LeftMargin ); 1109 double right_margin = convPx2mm( elem.RightMargin ); 1110 double top_margin = convPx2mm( elem.TopMargin ); 1111 double bottom_margin = convPx2mm( elem.BottomMargin ); 1112 if( ! pFirstPara ) 1113 { 1114 // use default page margins 1115 left_margin = 10; 1116 right_margin = 10; 1117 top_margin = 10; 1118 bottom_margin = 10; 1119 } 1120 1121 // round left/top margin to nearest mm 1122 left_margin = rtl_math_round( left_margin, 0, rtl_math_RoundingMode_Floor ); 1123 top_margin = rtl_math_round( top_margin, 0, rtl_math_RoundingMode_Floor ); 1124 // round (fuzzy) right/bottom margin to nearest cm 1125 right_margin = rtl_math_round( right_margin, right_margin >= 10 ? -1 : 0, rtl_math_RoundingMode_Floor ); 1126 bottom_margin = rtl_math_round( bottom_margin, bottom_margin >= 10 ? -1 : 0, rtl_math_RoundingMode_Floor ); 1127 1128 // set reasonable default in case of way too large margins 1129 // e.g. no paragraph case 1130 if( left_margin > page_width/2.0 - 10 ) 1131 left_margin = 10; 1132 if( right_margin > page_width/2.0 - 10 ) 1133 right_margin = 10; 1134 if( top_margin > page_height/2.0 - 10 ) 1135 top_margin = 10; 1136 if( bottom_margin > page_height/2.0 - 10 ) 1137 bottom_margin = 10; 1138 1139 // catch the weird cases 1140 if( left_margin < 0 ) 1141 left_margin = 0; 1142 if( right_margin < 0 ) 1143 right_margin = 0; 1144 if( top_margin < 0 ) 1145 top_margin = 0; 1146 if( bottom_margin < 0 ) 1147 bottom_margin = 0; 1148 1149 // widely differing margins are unlikely to be correct 1150 if( right_margin > left_margin*1.5 ) 1151 right_margin = left_margin; 1152 1153 elem.LeftMargin = convmm2Px( left_margin ); 1154 elem.RightMargin = convmm2Px( right_margin ); 1155 elem.TopMargin = convmm2Px( top_margin ); 1156 elem.BottomMargin = convmm2Px( bottom_margin ); 1157 1158 // get styles for paragraphs 1159 PropertyMap aPageProps; 1160 PropertyMap aPageLayoutProps; 1161 rtl::OUStringBuffer aBuf( 64 ); 1162 aPageLayoutProps[ USTR( "fo:page-width" ) ] = unitMMString( page_width ); 1163 aPageLayoutProps[ USTR( "fo:page-height" ) ] = unitMMString( page_height ); 1164 aPageLayoutProps[ USTR( "style:print-orientation" ) ] 1165 = elem.w < elem.h ? USTR( "portrait" ) : USTR( "landscape" ); 1166 aPageLayoutProps[ USTR( "fo:margin-top" ) ] = unitMMString( top_margin ); 1167 aPageLayoutProps[ USTR( "fo:margin-bottom" ) ] = unitMMString( bottom_margin ); 1168 aPageLayoutProps[ USTR( "fo:margin-left" ) ] = unitMMString( left_margin ); 1169 aPageLayoutProps[ USTR( "fo:margin-right" ) ] = unitMMString( right_margin ); 1170 aPageLayoutProps[ USTR( "style:writing-mode" ) ]= USTR( "lr-tb" ); 1171 1172 StyleContainer::Style aStyle( "style:page-layout", aPageProps); 1173 StyleContainer::Style aSubStyle( "style:page-layout-properties", aPageLayoutProps); 1174 aStyle.SubStyles.push_back(&aSubStyle); 1175 sal_Int32 nPageStyle = m_rStyleContainer.impl_getStyleId( aStyle, false ); 1176 1177 // create master page 1178 rtl::OUString aMasterPageLayoutName = m_rStyleContainer.getStyleName( nPageStyle ); 1179 aPageProps[ USTR( "style:page-layout-name" ) ] = aMasterPageLayoutName; 1180 StyleContainer::Style aMPStyle( "style:master-page", aPageProps ); 1181 StyleContainer::Style aHeaderStyle( "style:header", PropertyMap() ); 1182 StyleContainer::Style aFooterStyle( "style:footer", PropertyMap() ); 1183 if( elem.HeaderElement ) 1184 { 1185 elem.HeaderElement->visitedBy( *this, std::list<Element*>::iterator() ); 1186 aHeaderStyle.ContainedElement = elem.HeaderElement; 1187 aMPStyle.SubStyles.push_back( &aHeaderStyle ); 1188 } 1189 if( elem.FooterElement ) 1190 { 1191 elem.FooterElement->visitedBy( *this, std::list<Element*>::iterator() ); 1192 aFooterStyle.ContainedElement = elem.FooterElement; 1193 aMPStyle.SubStyles.push_back( &aFooterStyle ); 1194 } 1195 elem.StyleId = m_rStyleContainer.impl_getStyleId( aMPStyle,false ); 1196 1197 1198 rtl::OUString aMasterPageName = m_rStyleContainer.getStyleName( elem.StyleId ); 1199 1200 // create styles for children 1201 elem.applyToChildren(*this); 1202 1203 // no paragraph or other elements before the first paragraph 1204 if( ! pFirstPara ) 1205 { 1206 pFirstPara = m_rProcessor.getElementFactory()->createParagraphElement( NULL ); 1207 pFirstPara->Parent = &elem; 1208 elem.Children.push_front( pFirstPara ); 1209 } 1210 setFirstOnPage(*pFirstPara, m_rStyleContainer, aMasterPageName); 1211 } 1212 1213 void WriterXmlFinalizer::visit( DocumentElement& elem, const std::list< Element* >::const_iterator& ) 1214 { 1215 elem.applyToChildren(*this); 1216 } 1217 1218 } 1219