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