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_starmath.hxx" 30 31 #include "node.hxx" 32 #include "rect.hxx" 33 #include "symbol.hxx" 34 #include "smmod.hxx" 35 #include "document.hxx" 36 #include "view.hxx" 37 #include "mathtype.hxx" 38 39 #include <tools/gen.hxx> 40 #include <tools/fract.hxx> 41 #include <rtl/math.hxx> 42 #include <tools/color.hxx> 43 #include <vcl/metric.hxx> 44 #include <vcl/lineinfo.hxx> 45 #include <vcl/outdev.hxx> 46 #include <sfx2/module.hxx> 47 48 #include <math.h> 49 #include <float.h> 50 51 52 #define APPEND(str,ascii) str.AppendAscii(RTL_CONSTASCII_STRINGPARAM(ascii)) 53 54 // define this to draw rectangles for debugging 55 //#define SM_RECT_DEBUG 56 57 58 using ::rtl::OUString; 59 60 61 //////////////////////////////////////// 62 // SmTmpDevice 63 // Allows for font and color changes. The original settings will be restored 64 // in the destructor. 65 // It's main purpose is to allow for the "const" in the 'OutputDevice' 66 // argument in the 'Arrange' functions and restore changes made in the 'Draw' 67 // functions. 68 // Usually a MapMode of 1/100th mm will be used. 69 // 70 71 class SmTmpDevice 72 { 73 OutputDevice &rOutDev; 74 75 // disallow use of copy-constructor and assignment-operator 76 SmTmpDevice(const SmTmpDevice &rTmpDev); 77 SmTmpDevice & operator = (const SmTmpDevice &rTmpDev); 78 79 Color Impl_GetColor( const Color& rColor ); 80 81 public: 82 SmTmpDevice(OutputDevice &rTheDev, sal_Bool bUseMap100th_mm); 83 ~SmTmpDevice() { rOutDev.Pop(); } 84 85 void SetFont(const Font &rNewFont); 86 87 void SetLineColor( const Color& rColor ) { rOutDev.SetLineColor( Impl_GetColor(rColor) ); } 88 void SetFillColor( const Color& rColor ) { rOutDev.SetFillColor( Impl_GetColor(rColor) ); } 89 void SetTextColor( const Color& rColor ) { rOutDev.SetTextColor( Impl_GetColor(rColor) ); } 90 91 operator OutputDevice & () { return rOutDev; } 92 }; 93 94 95 SmTmpDevice::SmTmpDevice(OutputDevice &rTheDev, sal_Bool bUseMap100th_mm) : 96 rOutDev(rTheDev) 97 { 98 rOutDev.Push( PUSH_FONT | PUSH_MAPMODE | 99 PUSH_LINECOLOR | PUSH_FILLCOLOR | PUSH_TEXTCOLOR ); 100 if (bUseMap100th_mm && MAP_100TH_MM != rOutDev.GetMapMode().GetMapUnit()) 101 { 102 DBG_ERROR( "incorrect MapMode?" ); 103 rOutDev.SetMapMode( MAP_100TH_MM ); //Immer fuer 100% fomatieren 104 } 105 } 106 107 108 Color SmTmpDevice::Impl_GetColor( const Color& rColor ) 109 { 110 ColorData nNewCol = rColor.GetColor(); 111 if (COL_AUTO == nNewCol) 112 { 113 if (OUTDEV_PRINTER == rOutDev.GetOutDevType()) 114 nNewCol = COL_BLACK; 115 else 116 { 117 Color aBgCol( rOutDev.GetBackground().GetColor() ); 118 if (OUTDEV_WINDOW == rOutDev.GetOutDevType()) 119 aBgCol = ((Window &) rOutDev).GetDisplayBackground().GetColor(); 120 121 nNewCol = SM_MOD()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor; 122 123 Color aTmpColor( nNewCol ); 124 if (aBgCol.IsDark() && aTmpColor.IsDark()) 125 nNewCol = COL_WHITE; 126 else if (aBgCol.IsBright() && aTmpColor.IsBright()) 127 nNewCol = COL_BLACK; 128 } 129 } 130 return Color( nNewCol ); 131 } 132 133 134 void SmTmpDevice::SetFont(const Font &rNewFont) 135 { 136 rOutDev.SetFont( rNewFont ); 137 rOutDev.SetTextColor( Impl_GetColor( rNewFont.GetColor() ) ); 138 } 139 140 141 /////////////////////////////////////////////////////////////////////////// 142 143 144 SmNode::SmNode(SmNodeType eNodeType, const SmToken &rNodeToken) 145 { 146 eType = eNodeType; 147 eScaleMode = SCALE_NONE; 148 aNodeToken = rNodeToken; 149 nAccIndex = -1; 150 } 151 152 153 SmNode::~SmNode() 154 { 155 } 156 157 158 sal_Bool SmNode::IsVisible() const 159 { 160 return sal_False; 161 } 162 163 164 sal_uInt16 SmNode::GetNumSubNodes() const 165 { 166 return 0; 167 } 168 169 170 SmNode * SmNode::GetSubNode(sal_uInt16 /*nIndex*/) 171 { 172 return NULL; 173 } 174 175 176 SmNode * SmNode::GetLeftMost() 177 // returns leftmost node of current subtree. 178 //! (this assumes the one with index 0 is always the leftmost subnode 179 //! for the current node). 180 { 181 SmNode *pNode = GetNumSubNodes() > 0 ? 182 GetSubNode(0) : NULL; 183 184 return pNode ? pNode->GetLeftMost() : this; 185 } 186 187 188 void SmNode::SetPhantom(sal_Bool bIsPhantomP) 189 { 190 if (! (Flags() & FLG_VISIBLE)) 191 bIsPhantom = bIsPhantomP; 192 193 SmNode *pNode; 194 sal_uInt16 nSize = GetNumSubNodes(); 195 for (sal_uInt16 i = 0; i < nSize; i++) 196 if (NULL != (pNode = GetSubNode(i))) 197 pNode->SetPhantom(bIsPhantom); 198 } 199 200 201 void SmNode::SetColor(const Color& rColor) 202 { 203 if (! (Flags() & FLG_COLOR)) 204 GetFont().SetColor(rColor); 205 206 SmNode *pNode; 207 sal_uInt16 nSize = GetNumSubNodes(); 208 for (sal_uInt16 i = 0; i < nSize; i++) 209 if (NULL != (pNode = GetSubNode(i))) 210 pNode->SetColor(rColor); 211 } 212 213 214 void SmNode::SetAttribut(sal_uInt16 nAttrib) 215 { 216 if ( 217 (nAttrib == ATTR_BOLD && !(Flags() & FLG_BOLD)) || 218 (nAttrib == ATTR_ITALIC && !(Flags() & FLG_ITALIC)) 219 ) 220 { 221 nAttributes |= nAttrib; 222 } 223 224 SmNode *pNode; 225 sal_uInt16 nSize = GetNumSubNodes(); 226 for (sal_uInt16 i = 0; i < nSize; i++) 227 if (NULL != (pNode = GetSubNode(i))) 228 pNode->SetAttribut(nAttrib); 229 } 230 231 232 void SmNode::ClearAttribut(sal_uInt16 nAttrib) 233 { 234 if ( 235 (nAttrib == ATTR_BOLD && !(Flags() & FLG_BOLD)) || 236 (nAttrib == ATTR_ITALIC && !(Flags() & FLG_ITALIC)) 237 ) 238 { 239 nAttributes &= ~nAttrib; 240 } 241 242 SmNode *pNode; 243 sal_uInt16 nSize = GetNumSubNodes(); 244 for (sal_uInt16 i = 0; i < nSize; i++) 245 if (NULL != (pNode = GetSubNode(i))) 246 pNode->ClearAttribut(nAttrib); 247 } 248 249 250 void SmNode::SetFont(const SmFace &rFace) 251 { 252 if (!(Flags() & FLG_FONT)) 253 GetFont() = rFace; 254 255 SmNode *pNode; 256 sal_uInt16 nSize = GetNumSubNodes(); 257 for (sal_uInt16 i = 0; i < nSize; i++) 258 if (NULL != (pNode = GetSubNode(i))) 259 pNode->SetFont(rFace); 260 } 261 262 263 void SmNode::SetFontSize(const Fraction &rSize, sal_uInt16 nType) 264 //! 'rSize' is in units of pts 265 { 266 Size aFntSize; 267 268 if (!(Flags() & FLG_SIZE)) 269 { 270 Fraction aVal (SmPtsTo100th_mm(rSize.GetNumerator()), 271 rSize.GetDenominator()); 272 //long nHeight = ::rtl::math::round(aVal); 273 long nHeight = (long)aVal; 274 275 aFntSize = GetFont().GetSize(); 276 aFntSize.Width() = 0; 277 switch(nType) 278 { 279 case FNTSIZ_ABSOLUT: 280 aFntSize.Height() = nHeight; 281 break; 282 283 case FNTSIZ_PLUS: 284 aFntSize.Height() += nHeight; 285 break; 286 287 case FNTSIZ_MINUS: 288 aFntSize.Height() -= nHeight; 289 break; 290 291 case FNTSIZ_MULTIPLY: 292 aFntSize.Height() = (long) (Fraction(aFntSize.Height()) * rSize); 293 break; 294 295 case FNTSIZ_DIVIDE: 296 if (rSize != Fraction(0L)) 297 aFntSize.Height() = (long) (Fraction(aFntSize.Height()) / rSize); 298 break; 299 default: 300 break; 301 } 302 303 // check the requested size against maximum value 304 static int __READONLY_DATA nMaxVal = SmPtsTo100th_mm(128); 305 if (aFntSize.Height() > nMaxVal) 306 aFntSize.Height() = nMaxVal; 307 308 GetFont().SetSize(aFntSize); 309 } 310 311 SmNode *pNode; 312 sal_uInt16 nSize = GetNumSubNodes(); 313 for (sal_uInt16 i = 0; i < nSize; i++) 314 if (NULL != (pNode = GetSubNode(i))) 315 pNode->SetFontSize(rSize, nType); 316 } 317 318 319 void SmNode::SetSize(const Fraction &rSize) 320 { 321 GetFont() *= rSize; 322 323 SmNode *pNode; 324 sal_uInt16 nSize = GetNumSubNodes(); 325 for (sal_uInt16 i = 0; i < nSize; i++) 326 if (NULL != (pNode = GetSubNode(i))) 327 pNode->SetSize(rSize); 328 } 329 330 331 void SmNode::SetRectHorAlign(RectHorAlign eHorAlign, sal_Bool bApplyToSubTree ) 332 { 333 if (!(Flags() & FLG_HORALIGN)) 334 eRectHorAlign = eHorAlign; 335 336 if (bApplyToSubTree) 337 { 338 SmNode *pNode; 339 sal_uInt16 nSize = GetNumSubNodes(); 340 for (sal_uInt16 i = 0; i < nSize; i++) 341 if (NULL != (pNode = GetSubNode(i))) 342 pNode->SetRectHorAlign(eHorAlign); 343 } 344 } 345 346 347 void SmNode::PrepareAttributes() 348 { 349 GetFont().SetWeight((Attributes() & ATTR_BOLD) ? WEIGHT_BOLD : WEIGHT_NORMAL); 350 GetFont().SetItalic((Attributes() & ATTR_ITALIC) ? ITALIC_NORMAL : ITALIC_NONE); 351 } 352 353 354 void SmNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell) 355 { 356 #if OSL_DEBUG_LEVEL > 1 357 bIsDebug = sal_True; 358 #else 359 bIsDebug = sal_False; 360 #endif 361 bIsPhantom = sal_False; 362 nFlags = 0; 363 nAttributes = 0; 364 365 switch (rFormat.GetHorAlign()) 366 { case AlignLeft: eRectHorAlign = RHA_LEFT; break; 367 case AlignCenter: eRectHorAlign = RHA_CENTER; break; 368 case AlignRight: eRectHorAlign = RHA_RIGHT; break; 369 } 370 371 GetFont() = rFormat.GetFont(FNT_MATH); 372 //GetFont().SetCharSet(RTL_TEXTENCODING_SYMBOL); 373 DBG_ASSERT( GetFont().GetCharSet() == RTL_TEXTENCODING_UNICODE, 374 "unexpected CharSet" ); 375 GetFont().SetWeight(WEIGHT_NORMAL); 376 GetFont().SetItalic(ITALIC_NONE); 377 378 SmNode *pNode; 379 sal_uInt16 nSize = GetNumSubNodes(); 380 for (sal_uInt16 i = 0; i < nSize; i++) 381 if (NULL != (pNode = GetSubNode(i))) 382 pNode->Prepare(rFormat, rDocShell); 383 } 384 385 386 #if OSL_DEBUG_LEVEL > 1 387 void SmNode::ToggleDebug() const 388 // toggle 'bIsDebug' in current subtree 389 { 390 SmNode *pThis = (SmNode *) this; 391 392 pThis->bIsDebug = bIsDebug ? sal_False : sal_True; 393 394 SmNode *pNode; 395 sal_uInt16 nSize = GetNumSubNodes(); 396 for (sal_uInt16 i = 0; i < nSize; i++) 397 if (NULL != (pNode = pThis->GetSubNode(i))) 398 pNode->ToggleDebug(); 399 } 400 #endif 401 402 403 void SmNode::Move(const Point& rPosition) 404 { 405 if (rPosition.X() == 0 && rPosition.Y() == 0) 406 return; 407 408 SmRect::Move(rPosition); 409 410 SmNode *pNode; 411 sal_uInt16 nSize = GetNumSubNodes(); 412 for (sal_uInt16 i = 0; i < nSize; i++) 413 if (NULL != (pNode = GetSubNode(i))) 414 pNode->Move(rPosition); 415 } 416 417 418 void SmNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) 419 { 420 SmNode *pNode; 421 sal_uInt16 nSize = GetNumSubNodes(); 422 for (sal_uInt16 i = 0; i < nSize; i++) 423 if (NULL != (pNode = GetSubNode(i))) 424 pNode->Arrange(rDev, rFormat); 425 } 426 427 void SmNode::CreateTextFromNode(String &rText) 428 { 429 SmNode *pNode; 430 sal_uInt16 nSize = GetNumSubNodes(); 431 if (nSize > 1) 432 rText.Append('{'); 433 for (sal_uInt16 i = 0; i < nSize; i++) 434 if (NULL != (pNode = GetSubNode(i))) 435 pNode->CreateTextFromNode(rText); 436 if (nSize > 1) 437 { 438 rText.EraseTrailingChars(); 439 APPEND(rText,"} "); 440 } 441 } 442 443 444 void SmNode::AdaptToX(const OutputDevice &/*rDev*/, sal_uLong /*nWidth*/) 445 { 446 } 447 448 449 void SmNode::AdaptToY(const OutputDevice &/*rDev*/, sal_uLong /*nHeight*/) 450 { 451 } 452 453 454 void SmNode::Draw(OutputDevice &rDev, const Point &rPosition) const 455 { 456 if (IsPhantom()) 457 return; 458 459 const SmNode *pNode; 460 sal_uInt16 nSize = GetNumSubNodes(); 461 for (sal_uInt16 i = 0; i < nSize; i++) 462 if (NULL != (pNode = GetSubNode(i))) 463 { Point aOffset (pNode->GetTopLeft() - GetTopLeft()); 464 pNode->Draw(rDev, rPosition + aOffset); 465 } 466 467 #ifdef SM_RECT_DEBUG 468 if (!IsDebug()) 469 return; 470 471 int nRFlags = SM_RECT_CORE | SM_RECT_ITALIC | SM_RECT_LINES | SM_RECT_MID; 472 SmRect::Draw(rDev, rPosition, nRFlags); 473 #endif 474 } 475 476 const SmNode * SmNode::FindTokenAt(sal_uInt16 nRow, sal_uInt16 nCol) const 477 // returns (first) ** visible ** (sub)node with the tokens text at 478 // position 'nRow', 'nCol'. 479 //! (there should be exactly one such node if any) 480 { 481 if ( IsVisible() 482 && nRow == GetToken().nRow 483 && nCol >= GetToken().nCol && nCol < GetToken().nCol + GetToken().aText.Len()) 484 return this; 485 else 486 { 487 sal_uInt16 nNumSubNodes = GetNumSubNodes(); 488 for (sal_uInt16 i = 0; i < nNumSubNodes; i++) 489 { const SmNode *pNode = GetSubNode(i); 490 491 if (!pNode) 492 continue; 493 494 const SmNode *pResult = pNode->FindTokenAt(nRow, nCol); 495 if (pResult) 496 return pResult; 497 } 498 } 499 500 return 0; 501 } 502 503 504 const SmNode * SmNode::FindRectClosestTo(const Point &rPoint) const 505 { 506 long nDist = LONG_MAX; 507 const SmNode *pResult = 0; 508 509 if (IsVisible()) 510 pResult = this; 511 else 512 { 513 sal_uInt16 nNumSubNodes = GetNumSubNodes(); 514 for (sal_uInt16 i = 0; i < nNumSubNodes; i++) 515 { const SmNode *pNode = GetSubNode(i); 516 517 if (!pNode) 518 continue; 519 520 long nTmp; 521 const SmNode *pFound = pNode->FindRectClosestTo(rPoint); 522 if (pFound && (nTmp = pFound->OrientedDist(rPoint)) < nDist) 523 { nDist = nTmp; 524 pResult = pFound; 525 526 // quit immediately if 'rPoint' is inside the *should not 527 // overlap with other rectangles* part. 528 // This (partly) serves for getting the attributes in eg 529 // "bar overstrike a". 530 // ('nDist < 0' is used as *quick shot* to avoid evaluation of 531 // the following expression, where the result is already determined) 532 if (nDist < 0 && pFound->IsInsideRect(rPoint)) 533 break; 534 } 535 } 536 } 537 538 return pResult; 539 } 540 541 void SmNode::GetAccessibleText( String &/*rText*/ ) const 542 { 543 DBG_ERROR( "SmNode: GetAccessibleText not overloaded" ); 544 } 545 546 const SmNode * SmNode::FindNodeWithAccessibleIndex(xub_StrLen nAccIdx) const 547 { 548 const SmNode *pResult = 0; 549 550 sal_Int32 nIdx = GetAccessibleIndex(); 551 String aTxt; 552 if (nIdx >= 0) 553 GetAccessibleText( aTxt ); // get text if used in following 'if' statement 554 555 if (nIdx >= 0 556 && nIdx <= nAccIdx && nAccIdx < nIdx + aTxt.Len()) 557 pResult = this; 558 else 559 { 560 sal_uInt16 nNumSubNodes = GetNumSubNodes(); 561 for (sal_uInt16 i = 0; i < nNumSubNodes; i++) 562 { 563 const SmNode *pNode = GetSubNode(i); 564 if (!pNode) 565 continue; 566 567 pResult = pNode->FindNodeWithAccessibleIndex(nAccIdx); 568 if (pResult) 569 return pResult; 570 } 571 } 572 573 return pResult; 574 } 575 576 577 long SmNode::GetFormulaBaseline() const 578 { 579 DBG_ASSERT( 0, "This dummy implementation should not have been called." ); 580 return 0; 581 } 582 583 /////////////////////////////////////////////////////////////////////////// 584 585 SmStructureNode::SmStructureNode( const SmStructureNode &rNode ) : 586 SmNode( rNode.GetType(), rNode.GetToken() ) 587 { 588 sal_uLong i; 589 for (i = 0; i < aSubNodes.size(); i++) 590 delete aSubNodes[i]; 591 aSubNodes.resize(0); 592 593 sal_uLong nSize = rNode.aSubNodes.size(); 594 aSubNodes.resize( nSize ); 595 for (i = 0; i < nSize; ++i) 596 { 597 SmNode *pNode = rNode.aSubNodes[i]; 598 aSubNodes[i] = pNode ? new SmNode( *pNode ) : 0; 599 } 600 } 601 602 603 SmStructureNode::~SmStructureNode() 604 { 605 SmNode *pNode; 606 607 for (sal_uInt16 i = 0; i < GetNumSubNodes(); i++) 608 if (NULL != (pNode = GetSubNode(i))) 609 delete pNode; 610 } 611 612 613 SmStructureNode & SmStructureNode::operator = ( const SmStructureNode &rNode ) 614 { 615 SmNode::operator = ( rNode ); 616 617 sal_uLong i; 618 for (i = 0; i < aSubNodes.size(); i++) 619 delete aSubNodes[i]; 620 aSubNodes.resize(0); 621 622 sal_uLong nSize = rNode.aSubNodes.size(); 623 aSubNodes.resize( nSize ); 624 for (i = 0; i < nSize; ++i) 625 { 626 SmNode *pNode = rNode.aSubNodes[i]; 627 aSubNodes[i] = pNode ? new SmNode( *pNode ) : 0; 628 } 629 630 return *this; 631 } 632 633 634 void SmStructureNode::SetSubNodes(SmNode *pFirst, SmNode *pSecond, SmNode *pThird) 635 { 636 size_t nSize = pThird ? 3 : (pSecond ? 2 : (pFirst ? 1 : 0)); 637 aSubNodes.resize( nSize ); 638 if (pFirst) 639 aSubNodes[0] = pFirst; 640 if (pSecond) 641 aSubNodes[1] = pSecond; 642 if (pThird) 643 aSubNodes[2] = pThird; 644 } 645 646 647 void SmStructureNode::SetSubNodes(const SmNodeArray &rNodeArray) 648 { 649 aSubNodes = rNodeArray; 650 } 651 652 653 sal_Bool SmStructureNode::IsVisible() const 654 { 655 return sal_False; 656 } 657 658 659 sal_uInt16 SmStructureNode::GetNumSubNodes() const 660 { 661 return (sal_uInt16) aSubNodes.size(); 662 } 663 664 665 SmNode * SmStructureNode::GetSubNode(sal_uInt16 nIndex) 666 { 667 return aSubNodes[nIndex]; 668 } 669 670 671 void SmStructureNode::GetAccessibleText( String &rText ) const 672 { 673 sal_uInt16 nNodes = GetNumSubNodes(); 674 for (sal_uInt16 i = 0; i < nNodes; ++i) 675 { 676 const SmNode *pNode = ((SmStructureNode *) this)->GetSubNode(i); 677 if (pNode) 678 { 679 if (pNode->IsVisible()) 680 ((SmStructureNode *) pNode)->nAccIndex = rText.Len(); 681 pNode->GetAccessibleText( rText ); 682 // if (rText.Len() && ' ' != rText.GetChar( rText.Len() - 1 )) 683 // rText += String::CreateFromAscii( " " ); 684 } 685 } 686 } 687 688 /////////////////////////////////////////////////////////////////////////// 689 690 691 sal_Bool SmVisibleNode::IsVisible() const 692 { 693 return sal_True; 694 } 695 696 697 sal_uInt16 SmVisibleNode::GetNumSubNodes() const 698 { 699 return 0; 700 } 701 702 703 SmNode * SmVisibleNode::GetSubNode(sal_uInt16 /*nIndex*/) 704 { 705 return NULL; 706 } 707 708 709 /////////////////////////////////////////////////////////////////////////// 710 711 void SmGraphicNode::GetAccessibleText( String &rText ) const 712 { 713 rText += GetToken().aText; 714 } 715 716 /////////////////////////////////////////////////////////////////////////// 717 718 719 void SmExpressionNode::CreateTextFromNode(String &rText) 720 { 721 SmNode *pNode; 722 sal_uInt16 nSize = GetNumSubNodes(); 723 if (nSize > 1) 724 rText.Append('{'); 725 for (sal_uInt16 i = 0; i < nSize; i++) 726 if (NULL != (pNode = GetSubNode(i))) 727 { 728 pNode->CreateTextFromNode(rText); 729 //Just a bit of foo to make unary +asd -asd +-asd -+asd look nice 730 if (pNode->GetType() == NMATH) 731 if ((nSize != 2) || ((rText.GetChar(rText.Len()-1) != '+') && 732 (rText.GetChar(rText.Len()-1) != '-'))) 733 rText.Append(' '); 734 } 735 736 if (nSize > 1) 737 { 738 rText.EraseTrailingChars(); 739 APPEND(rText,"} "); 740 } 741 } 742 743 744 /////////////////////////////////////////////////////////////////////////// 745 746 void SmTableNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) 747 // arranges all subnodes in one column 748 { 749 Point rPosition; 750 751 SmNode *pNode; 752 sal_uInt16 nSize = GetNumSubNodes(); 753 754 // make distance depend on font size 755 long nDist = +(rFormat.GetDistance(DIS_VERTICAL) 756 * GetFont().GetSize().Height()) / 100L; 757 758 if (nSize < 1) 759 return; 760 761 // arrange subnodes and get maximum width of them 762 long nMaxWidth = 0, 763 nTmp; 764 sal_uInt16 i; 765 for (i = 0; i < nSize; i++) 766 if (NULL != (pNode = GetSubNode(i))) 767 { pNode->Arrange(rDev, rFormat); 768 if ((nTmp = pNode->GetItalicWidth()) > nMaxWidth) 769 nMaxWidth = nTmp; 770 } 771 772 Point aPos; 773 SmRect::operator = (SmRect(nMaxWidth, 1)); 774 for (i = 0; i < nSize; i++) 775 { if (NULL != (pNode = GetSubNode(i))) 776 { const SmRect &rNodeRect = pNode->GetRect(); 777 const SmNode *pCoNode = pNode->GetLeftMost(); 778 //SmTokenType eType = pCoNode->GetToken().eType; 779 RectHorAlign eHorAlign = pCoNode->GetRectHorAlign(); 780 781 aPos = rNodeRect.AlignTo(*this, RP_BOTTOM, 782 eHorAlign, RVA_BASELINE); 783 if (i) 784 aPos.Y() += nDist; 785 pNode->MoveTo(aPos); 786 ExtendBy(rNodeRect, nSize > 1 ? RCP_NONE : RCP_ARG); 787 } 788 } 789 // --> 4.7.2010 #i972# 790 if (HasBaseline()) 791 nFormulaBaseline = GetBaseline(); 792 else 793 { 794 SmTmpDevice aTmpDev ((OutputDevice &) rDev, sal_True); 795 aTmpDev.SetFont(GetFont()); 796 797 SmRect aRect = (SmRect(aTmpDev, &rFormat, C2S("a"), 798 GetFont().GetBorderWidth())); 799 nFormulaBaseline = GetAlignM(); 800 // move from middle position by constant - distance 801 // between middle and baseline for single letter 802 nFormulaBaseline += aRect.GetBaseline() - aRect.GetAlignM(); 803 } 804 // <-- 805 } 806 807 808 SmNode * SmTableNode::GetLeftMost() 809 { 810 return this; 811 } 812 813 814 long SmTableNode::GetFormulaBaseline() const 815 { 816 return nFormulaBaseline; 817 } 818 819 820 /**************************************************************************/ 821 822 823 void SmLineNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell) 824 { 825 SmNode::Prepare(rFormat, rDocShell); 826 827 //! wir verwenden hier den 'FNT_VARIABLE' Font, da er vom Ascent und Descent 828 //! ia besser zum Rest der Formel passt als der 'FNT_MATH' Font. 829 GetFont() = rFormat.GetFont(FNT_VARIABLE); 830 Flags() |= FLG_FONT; 831 } 832 833 834 /**************************************************************************/ 835 836 837 void SmLineNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) 838 // arranges all subnodes in one row with some extra space between 839 { 840 SmNode *pNode; 841 sal_uInt16 nSize = GetNumSubNodes(); 842 sal_uInt16 i; 843 for (i = 0; i < nSize; i++) 844 if (NULL != (pNode = GetSubNode(i))) 845 pNode->Arrange(rDev, rFormat); 846 847 SmTmpDevice aTmpDev ((OutputDevice &) rDev, sal_True); 848 aTmpDev.SetFont(GetFont()); 849 850 if (nSize < 1) 851 { 852 // provide an empty rectangle with alignment parameters for the "current" 853 // font (in order to make "a^1 {}_2^3 a_4" work correct, that is, have the 854 // same sub-/supscript positions.) 855 //! be sure to use a character that has explicitly defined HiAttribut 856 //! line in rect.cxx such as 'a' in order to make 'vec a' look same to 857 //! 'vec {a}'. 858 SmRect::operator = (SmRect(aTmpDev, &rFormat, C2S("a"), 859 GetFont().GetBorderWidth())); 860 // make sure that the rectangle occupies (almost) no space 861 SetWidth(1); 862 SetItalicSpaces(0, 0); 863 return; 864 } 865 866 // make distance depend on font size 867 long nDist = (rFormat.GetDistance(DIS_HORIZONTAL) * GetFont().GetSize().Height()) / 100L; 868 if (!IsUseExtraSpaces()) 869 nDist = 0; 870 871 Point aPos; 872 // copy the first node into LineNode and extend by the others 873 if (NULL != (pNode = GetSubNode(0))) 874 SmRect::operator = (pNode->GetRect()); 875 876 for (i = 1; i < nSize; i++) 877 if (NULL != (pNode = GetSubNode(i))) 878 { 879 aPos = pNode->AlignTo(*this, RP_RIGHT, RHA_CENTER, RVA_BASELINE); 880 881 // add horizontal space to the left for each but the first sub node 882 aPos.X() += nDist; 883 884 pNode->MoveTo(aPos); 885 ExtendBy( *pNode, RCP_XOR ); 886 } 887 } 888 889 890 /**************************************************************************/ 891 892 893 void SmExpressionNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) 894 // as 'SmLineNode::Arrange' but keeps alignment of leftmost subnode 895 { 896 SmLineNode::Arrange(rDev, rFormat); 897 898 // copy alignment of leftmost subnode if any 899 SmNode *pNode = GetLeftMost(); 900 if (pNode) 901 SetRectHorAlign(pNode->GetRectHorAlign(), sal_False); 902 } 903 904 905 /**************************************************************************/ 906 907 908 void SmUnHorNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) 909 { 910 sal_Bool bIsPostfix = GetToken().eType == TFACT; 911 912 SmNode *pOper = GetSubNode(bIsPostfix ? 1 : 0), 913 *pBody = GetSubNode(bIsPostfix ? 0 : 1); 914 DBG_ASSERT(pOper, "Sm: NULL pointer"); 915 DBG_ASSERT(pBody, "Sm: NULL pointer"); 916 917 pOper->SetSize(Fraction (rFormat.GetRelSize(SIZ_OPERATOR), 100)); 918 pOper->Arrange(rDev, rFormat); 919 pBody->Arrange(rDev, rFormat); 920 921 Point aPos = pOper->AlignTo(*pBody, bIsPostfix ? RP_RIGHT : RP_LEFT, 922 RHA_CENTER, RVA_BASELINE); 923 // add a bit space between operator and argument 924 // (worst case -{1 over 2} where - and over have almost no space inbetween) 925 long nDelta = pOper->GetFont().GetSize().Height() / 20; 926 if (bIsPostfix) 927 aPos.X() += nDelta; 928 else 929 aPos.X() -= nDelta; 930 pOper->MoveTo(aPos); 931 932 SmRect::operator = (*pBody); 933 long nOldBot = GetBottom(); 934 935 ExtendBy(*pOper, RCP_XOR); 936 937 // workaround for Bug 50865: "a^2 a^+2" have different baselines 938 // for exponents (if size of exponent is large enough) 939 SetBottom(nOldBot); 940 } 941 942 943 /**************************************************************************/ 944 945 946 void SmRootNode::GetHeightVerOffset(const SmRect &rRect, 947 long &rHeight, long &rVerOffset) const 948 // calculate height and vertical offset of root sign suitable for 'rRect' 949 { 950 rVerOffset = (rRect.GetBottom() - rRect.GetAlignB()) / 2; 951 rHeight = rRect.GetHeight() - rVerOffset; 952 953 DBG_ASSERT(rHeight >= 0, "Sm : Ooops..."); 954 DBG_ASSERT(rVerOffset >= 0, "Sm : Ooops..."); 955 } 956 957 958 Point SmRootNode::GetExtraPos(const SmRect &rRootSymbol, 959 const SmRect &rExtra) const 960 { 961 const Size &rSymSize = rRootSymbol.GetSize(); 962 963 Point aPos = rRootSymbol.GetTopLeft() 964 + Point((rSymSize.Width() * 70) / 100, 965 (rSymSize.Height() * 52) / 100); 966 967 // from this calculate topleft edge of 'rExtra' 968 aPos.X() -= rExtra.GetWidth() + rExtra.GetItalicRightSpace(); 969 aPos.Y() -= rExtra.GetHeight(); 970 // if there's enough space move a bit less to the right 971 // examples: "nroot i a", "nroot j a" 972 // (it looks better if we don't use italic-spaces here) 973 long nX = rRootSymbol.GetLeft() + (rSymSize.Width() * 30) / 100; 974 if (aPos.X() > nX) 975 aPos.X() = nX; 976 977 return aPos; 978 } 979 980 981 void SmRootNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) 982 { 983 //! pExtra needs to have the smaller index than pRootSym in order to 984 //! not to get the root symbol but the pExtra when clicking on it in the 985 //! GraphicWindow. (That is because of the simplicity of the algorithm 986 //! that finds the node corresponding to a mouseclick in the window.) 987 SmNode *pExtra = GetSubNode(0), 988 *pRootSym = GetSubNode(1), 989 *pBody = GetSubNode(2); 990 DBG_ASSERT(pRootSym, "Sm: NULL pointer"); 991 DBG_ASSERT(pBody, "Sm: NULL pointer"); 992 993 pBody->Arrange(rDev, rFormat); 994 995 long nHeight, 996 nVerOffset; 997 GetHeightVerOffset(*pBody, nHeight, nVerOffset); 998 nHeight += rFormat.GetDistance(DIS_ROOT) 999 * GetFont().GetSize().Height() / 100L; 1000 1001 // font specialist advised to change the width first 1002 pRootSym->AdaptToY(rDev, nHeight); 1003 pRootSym->AdaptToX(rDev, pBody->GetItalicWidth()); 1004 1005 pRootSym->Arrange(rDev, rFormat); 1006 1007 Point aPos = pRootSym->AlignTo(*pBody, RP_LEFT, RHA_CENTER, RVA_BASELINE); 1008 //! overrride calulated vertical position 1009 aPos.Y() = pRootSym->GetTop() + pBody->GetBottom() - pRootSym->GetBottom(); 1010 aPos.Y() -= nVerOffset; 1011 pRootSym->MoveTo(aPos); 1012 1013 if (pExtra) 1014 { pExtra->SetSize(Fraction(rFormat.GetRelSize(SIZ_INDEX), 100)); 1015 pExtra->Arrange(rDev, rFormat); 1016 1017 aPos = GetExtraPos(*pRootSym, *pExtra); 1018 pExtra->MoveTo(aPos); 1019 } 1020 1021 SmRect::operator = (*pBody); 1022 ExtendBy(*pRootSym, RCP_THIS); 1023 if (pExtra) 1024 ExtendBy(*pExtra, RCP_THIS, (sal_Bool) sal_True); 1025 } 1026 1027 1028 void SmRootNode::CreateTextFromNode(String &rText) 1029 { 1030 SmNode *pExtra = GetSubNode(0); 1031 if (pExtra) 1032 { 1033 APPEND(rText,"nroot "); 1034 pExtra->CreateTextFromNode(rText); 1035 } 1036 else 1037 APPEND(rText,"sqrt "); 1038 GetSubNode(2)->CreateTextFromNode(rText); 1039 } 1040 1041 1042 /**************************************************************************/ 1043 1044 1045 void SmBinHorNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) 1046 { 1047 SmNode *pLeft = GetSubNode(0), 1048 *pOper = GetSubNode(1), 1049 *pRight = GetSubNode(2); 1050 DBG_ASSERT(pLeft != NULL, "Sm: NULL pointer"); 1051 DBG_ASSERT(pOper != NULL, "Sm: NULL pointer"); 1052 DBG_ASSERT(pRight != NULL, "Sm: NULL pointer"); 1053 1054 pOper->SetSize(Fraction (rFormat.GetRelSize(SIZ_OPERATOR), 100)); 1055 1056 pLeft ->Arrange(rDev, rFormat); 1057 pOper ->Arrange(rDev, rFormat); 1058 pRight->Arrange(rDev, rFormat); 1059 1060 const SmRect &rOpRect = pOper->GetRect(); 1061 1062 long nDist = (rOpRect.GetWidth() * 1063 rFormat.GetDistance(DIS_HORIZONTAL)) / 100L; 1064 1065 SmRect::operator = (*pLeft); 1066 1067 Point aPos; 1068 aPos = pOper->AlignTo(*this, RP_RIGHT, RHA_CENTER, RVA_BASELINE); 1069 aPos.X() += nDist; 1070 pOper->MoveTo(aPos); 1071 ExtendBy(*pOper, RCP_XOR); 1072 1073 aPos = pRight->AlignTo(*this, RP_RIGHT, RHA_CENTER, RVA_BASELINE); 1074 aPos.X() += nDist; 1075 1076 pRight->MoveTo(aPos); 1077 ExtendBy(*pRight, RCP_XOR); 1078 } 1079 1080 1081 /**************************************************************************/ 1082 1083 1084 void SmBinVerNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) 1085 { 1086 SmNode *pNum = GetSubNode(0), 1087 *pLine = GetSubNode(1), 1088 *pDenom = GetSubNode(2); 1089 DBG_ASSERT(pNum, "Sm : NULL pointer"); 1090 DBG_ASSERT(pLine, "Sm : NULL pointer"); 1091 DBG_ASSERT(pDenom, "Sm : NULL pointer"); 1092 1093 sal_Bool bIsTextmode = rFormat.IsTextmode(); 1094 if (bIsTextmode) 1095 { 1096 Fraction aFraction(rFormat.GetRelSize(SIZ_INDEX), 100); 1097 pNum ->SetSize(aFraction); 1098 pLine ->SetSize(aFraction); 1099 pDenom->SetSize(aFraction); 1100 } 1101 1102 pNum ->Arrange(rDev, rFormat); 1103 pDenom->Arrange(rDev, rFormat); 1104 1105 long nFontHeight = GetFont().GetSize().Height(), 1106 nExtLen = nFontHeight * rFormat.GetDistance(DIS_FRACTION) / 100L, 1107 nThick = nFontHeight * rFormat.GetDistance(DIS_STROKEWIDTH) / 100L, 1108 nWidth = Max(pNum->GetItalicWidth(), pDenom->GetItalicWidth()), 1109 nNumDist = bIsTextmode ? 0 : 1110 nFontHeight * rFormat.GetDistance(DIS_NUMERATOR) / 100L, 1111 nDenomDist = bIsTextmode ? 0 : 1112 nFontHeight * rFormat.GetDistance(DIS_DENOMINATOR) / 100L; 1113 1114 // font specialist advised to change the width first 1115 pLine->AdaptToY(rDev, nThick); 1116 pLine->AdaptToX(rDev, nWidth + 2 * nExtLen); 1117 pLine->Arrange(rDev, rFormat); 1118 1119 // get horizontal alignment for numerator 1120 const SmNode *pLM = pNum->GetLeftMost(); 1121 RectHorAlign eHorAlign = pLM->GetRectHorAlign(); 1122 1123 // move numerator to its position 1124 Point aPos = pNum->AlignTo(*pLine, RP_TOP, eHorAlign, RVA_BASELINE); 1125 aPos.Y() -= nNumDist; 1126 pNum->MoveTo(aPos); 1127 1128 // get horizontal alignment for denominator 1129 pLM = pDenom->GetLeftMost(); 1130 eHorAlign = pLM->GetRectHorAlign(); 1131 1132 // move denominator to its position 1133 aPos = pDenom->AlignTo(*pLine, RP_BOTTOM, eHorAlign, RVA_BASELINE); 1134 aPos.Y() += nDenomDist; 1135 pDenom->MoveTo(aPos); 1136 1137 SmRect::operator = (*pNum); 1138 ExtendBy(*pDenom, RCP_NONE).ExtendBy(*pLine, RCP_NONE, pLine->GetCenterY()); 1139 } 1140 1141 void SmBinVerNode::CreateTextFromNode(String &rText) 1142 { 1143 SmNode *pNum = GetSubNode(0), 1144 // *pLine = GetSubNode(1), 1145 *pDenom = GetSubNode(2); 1146 pNum->CreateTextFromNode(rText); 1147 APPEND(rText,"over "); 1148 pDenom->CreateTextFromNode(rText); 1149 } 1150 1151 1152 SmNode * SmBinVerNode::GetLeftMost() 1153 { 1154 return this; 1155 } 1156 1157 1158 /**************************************************************************/ 1159 1160 1161 double Det(const Point &rHeading1, const Point &rHeading2) 1162 // gibt den Wert der durch die beiden Punkte gebildeten Determinante 1163 // zurueck 1164 { 1165 return rHeading1.X() * rHeading2.Y() - rHeading1.Y() * rHeading2.X(); 1166 } 1167 1168 1169 sal_Bool IsPointInLine(const Point &rPoint1, 1170 const Point &rPoint2, const Point &rHeading2) 1171 // ergibt sal_True genau dann, wenn der Punkt 'rPoint1' zu der Gerade gehoert die 1172 // durch den Punkt 'rPoint2' geht und den Richtungsvektor 'rHeading2' hat 1173 { 1174 DBG_ASSERT(rHeading2 != Point(), "Sm : 0 vector"); 1175 1176 sal_Bool bRes = sal_False; 1177 const double eps = 5.0 * DBL_EPSILON; 1178 1179 double fLambda; 1180 if (labs(rHeading2.X()) > labs(rHeading2.Y())) 1181 { 1182 fLambda = (rPoint1.X() - rPoint2.X()) / (double) rHeading2.X(); 1183 bRes = fabs(rPoint1.Y() - (rPoint2.Y() + fLambda * rHeading2.Y())) < eps; 1184 } 1185 else 1186 { 1187 fLambda = (rPoint1.Y() - rPoint2.Y()) / (double) rHeading2.Y(); 1188 bRes = fabs(rPoint1.X() - (rPoint2.X() + fLambda * rHeading2.X())) < eps; 1189 } 1190 1191 return bRes; 1192 } 1193 1194 1195 sal_uInt16 GetLineIntersectionPoint(Point &rResult, 1196 const Point& rPoint1, const Point &rHeading1, 1197 const Point& rPoint2, const Point &rHeading2) 1198 { 1199 DBG_ASSERT(rHeading1 != Point(), "Sm : 0 vector"); 1200 DBG_ASSERT(rHeading2 != Point(), "Sm : 0 vector"); 1201 1202 sal_uInt16 nRes = 1; 1203 const double eps = 5.0 * DBL_EPSILON; 1204 1205 // sind die Richtumgsvektoren linear abhaengig ? 1206 double fDet = Det(rHeading1, rHeading2); 1207 if (fabs(fDet) < eps) 1208 { 1209 nRes = IsPointInLine(rPoint1, rPoint2, rHeading2) ? USHRT_MAX : 0; 1210 rResult = nRes ? rPoint1 : Point(); 1211 } 1212 else 1213 { 1214 // hier achten wir nicht auf Rechengenauigkeit 1215 // (das wuerde aufwendiger und lohnt sich hier kaum) 1216 double fLambda = ( (rPoint1.Y() - rPoint2.Y()) * rHeading2.X() 1217 - (rPoint1.X() - rPoint2.X()) * rHeading2.Y()) 1218 / fDet; 1219 rResult = Point(rPoint1.X() + (long) (fLambda * rHeading1.X()), 1220 rPoint1.Y() + (long) (fLambda * rHeading1.Y())); 1221 } 1222 1223 return nRes; 1224 } 1225 1226 1227 1228 SmBinDiagonalNode::SmBinDiagonalNode(const SmToken &rNodeToken) 1229 : SmStructureNode(NBINDIAGONAL, rNodeToken) 1230 { 1231 bAscending = sal_False; 1232 SetNumSubNodes(3); 1233 } 1234 1235 1236 void SmBinDiagonalNode::GetOperPosSize(Point &rPos, Size &rSize, 1237 const Point &rDiagPoint, double fAngleDeg) const 1238 // gibt die Position und Groesse fuer den Diagonalstrich zurueck. 1239 // Vor.: das SmRect des Nodes gibt die Begrenzung vor(!), muss also selbst 1240 // bereits bekannt sein. 1241 1242 { 1243 const double fPi = 3.1415926535897932384626433; 1244 double fAngleRad = fAngleDeg / 180.0 * fPi; 1245 long nRectLeft = GetItalicLeft(), 1246 nRectRight = GetItalicRight(), 1247 nRectTop = GetTop(), 1248 nRectBottom = GetBottom(); 1249 Point aRightHdg (100, 0), 1250 aDownHdg (0, 100), 1251 aDiagHdg ( (long)(100.0 * cos(fAngleRad)), 1252 (long)(-100.0 * sin(fAngleRad)) ); 1253 1254 long nLeft, nRight, nTop, nBottom; // Raender des Rechtecks fuer die 1255 // Diagonale 1256 Point aPoint; 1257 if (IsAscending()) 1258 { 1259 // 1260 // obere rechte Ecke bestimmen 1261 // 1262 GetLineIntersectionPoint(aPoint, 1263 Point(nRectLeft, nRectTop), aRightHdg, 1264 rDiagPoint, aDiagHdg); 1265 // 1266 // gibt es einen Schnittpunkt mit dem oberen Rand ? 1267 if (aPoint.X() <= nRectRight) 1268 { 1269 nRight = aPoint.X(); 1270 nTop = nRectTop; 1271 } 1272 else 1273 { 1274 // es muss einen Schnittpunkt mit dem rechten Rand geben! 1275 GetLineIntersectionPoint(aPoint, 1276 Point(nRectRight, nRectTop), aDownHdg, 1277 rDiagPoint, aDiagHdg); 1278 1279 nRight = nRectRight; 1280 nTop = aPoint.Y(); 1281 } 1282 1283 // 1284 // untere linke Ecke bestimmen 1285 // 1286 GetLineIntersectionPoint(aPoint, 1287 Point(nRectLeft, nRectBottom), aRightHdg, 1288 rDiagPoint, aDiagHdg); 1289 // 1290 // gibt es einen Schnittpunkt mit dem unteren Rand ? 1291 if (aPoint.X() >= nRectLeft) 1292 { 1293 nLeft = aPoint.X(); 1294 nBottom = nRectBottom; 1295 } 1296 else 1297 { 1298 // es muss einen Schnittpunkt mit dem linken Rand geben! 1299 GetLineIntersectionPoint(aPoint, 1300 Point(nRectLeft, nRectTop), aDownHdg, 1301 rDiagPoint, aDiagHdg); 1302 1303 nLeft = nRectLeft; 1304 nBottom = aPoint.Y(); 1305 } 1306 } 1307 else 1308 { 1309 // 1310 // obere linke Ecke bestimmen 1311 // 1312 GetLineIntersectionPoint(aPoint, 1313 Point(nRectLeft, nRectTop), aRightHdg, 1314 rDiagPoint, aDiagHdg); 1315 // 1316 // gibt es einen Schnittpunkt mit dem oberen Rand ? 1317 if (aPoint.X() >= nRectLeft) 1318 { 1319 nLeft = aPoint.X(); 1320 nTop = nRectTop; 1321 } 1322 else 1323 { 1324 // es muss einen Schnittpunkt mit dem linken Rand geben! 1325 GetLineIntersectionPoint(aPoint, 1326 Point(nRectLeft, nRectTop), aDownHdg, 1327 rDiagPoint, aDiagHdg); 1328 1329 nLeft = nRectLeft; 1330 nTop = aPoint.Y(); 1331 } 1332 1333 // 1334 // untere rechte Ecke bestimmen 1335 // 1336 GetLineIntersectionPoint(aPoint, 1337 Point(nRectLeft, nRectBottom), aRightHdg, 1338 rDiagPoint, aDiagHdg); 1339 // 1340 // gibt es einen Schnittpunkt mit dem unteren Rand ? 1341 if (aPoint.X() <= nRectRight) 1342 { 1343 nRight = aPoint.X(); 1344 nBottom = nRectBottom; 1345 } 1346 else 1347 { 1348 // es muss einen Schnittpunkt mit dem rechten Rand geben! 1349 GetLineIntersectionPoint(aPoint, 1350 Point(nRectRight, nRectTop), aDownHdg, 1351 rDiagPoint, aDiagHdg); 1352 1353 nRight = nRectRight; 1354 nBottom = aPoint.Y(); 1355 } 1356 } 1357 1358 rSize = Size(nRight - nLeft + 1, nBottom - nTop + 1); 1359 rPos.X() = nLeft; 1360 rPos.Y() = nTop; 1361 } 1362 1363 1364 void SmBinDiagonalNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) 1365 { 1366 //! die beiden Argumente muessen in den Subnodes vor dem Operator kommen, 1367 //! damit das anklicken im GraphicWindow den FormulaCursor richtig setzt 1368 //! (vgl SmRootNode) 1369 SmNode *pLeft = GetSubNode(0), 1370 *pRight = GetSubNode(1); 1371 DBG_ASSERT(pLeft, "Sm : NULL pointer"); 1372 DBG_ASSERT(pRight, "Sm : NULL pointer"); 1373 1374 DBG_ASSERT(GetSubNode(2)->GetType() == NPOLYLINE, "Sm : falscher Nodetyp"); 1375 SmPolyLineNode *pOper = (SmPolyLineNode *) GetSubNode(2); 1376 DBG_ASSERT(pOper, "Sm : NULL pointer"); 1377 1378 //! some routines being called extract some info from the OutputDevice's 1379 //! font (eg the space to be used for borders OR the font name(!!)). 1380 //! Thus the font should reflect the needs and has to be set! 1381 SmTmpDevice aTmpDev ((OutputDevice &) rDev, sal_True); 1382 aTmpDev.SetFont(GetFont()); 1383 1384 pLeft->Arrange(aTmpDev, rFormat); 1385 pRight->Arrange(aTmpDev, rFormat); 1386 1387 // implizit die Weite (incl Rand) des Diagonalstrichs ermitteln 1388 pOper->Arrange(aTmpDev, rFormat); 1389 1390 long nDelta = pOper->GetWidth() * 8 / 10; 1391 1392 // TopLeft Position vom rechten Argument ermitteln 1393 Point aPos; 1394 aPos.X() = pLeft->GetItalicRight() + nDelta + pRight->GetItalicLeftSpace(); 1395 if (IsAscending()) 1396 aPos.Y() = pLeft->GetBottom() + nDelta; 1397 else 1398 aPos.Y() = pLeft->GetTop() - nDelta - pRight->GetHeight(); 1399 1400 pRight->MoveTo(aPos); 1401 1402 // neue Baseline bestimmen 1403 long nTmpBaseline = IsAscending() ? (pLeft->GetBottom() + pRight->GetTop()) / 2 1404 : (pLeft->GetTop() + pRight->GetBottom()) / 2; 1405 Point aLogCenter ((pLeft->GetItalicRight() + pRight->GetItalicLeft()) / 2, 1406 nTmpBaseline); 1407 1408 SmRect::operator = (*pLeft); 1409 ExtendBy(*pRight, RCP_NONE); 1410 1411 1412 // Position und Groesse des Diagonalstrich ermitteln 1413 Size aTmpSize; 1414 GetOperPosSize(aPos, aTmpSize, aLogCenter, IsAscending() ? 60.0 : -60.0); 1415 1416 // font specialist advised to change the width first 1417 pOper->AdaptToY(aTmpDev, aTmpSize.Height()); 1418 pOper->AdaptToX(aTmpDev, aTmpSize.Width()); 1419 // und diese wirksam machen 1420 pOper->Arrange(aTmpDev, rFormat); 1421 1422 pOper->MoveTo(aPos); 1423 1424 ExtendBy(*pOper, RCP_NONE, nTmpBaseline); 1425 } 1426 1427 1428 /**************************************************************************/ 1429 1430 1431 void SmSubSupNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) 1432 { 1433 DBG_ASSERT(GetNumSubNodes() == 1 + SUBSUP_NUM_ENTRIES, 1434 "Sm: falsche Anzahl von subnodes"); 1435 1436 SmNode *pBody = GetBody(); 1437 DBG_ASSERT(pBody, "Sm: NULL pointer"); 1438 1439 long nOrigHeight = pBody->GetFont().GetSize().Height(); 1440 1441 pBody->Arrange(rDev, rFormat); 1442 1443 const SmRect &rBodyRect = pBody->GetRect(); 1444 SmRect::operator = (rBodyRect); 1445 1446 // line that separates sub- and supscript rectangles 1447 long nDelimLine = SmFromTo(GetAlignB(), GetAlignT(), 0.4); 1448 1449 Point aPos; 1450 long nDelta, nDist; 1451 1452 // iterate over all possible sub-/supscripts 1453 SmRect aTmpRect (rBodyRect); 1454 for (int i = 0; i < SUBSUP_NUM_ENTRIES; i++) 1455 { SmSubSup eSubSup = (SmSubSup) i; // cast 1456 SmNode *pSubSup = GetSubSup(eSubSup); 1457 1458 if (!pSubSup) 1459 continue; 1460 1461 // switch position of limits if we are in textmode 1462 if (rFormat.IsTextmode() && (GetToken().nGroup & TGLIMIT)) 1463 switch (eSubSup) 1464 { case CSUB: eSubSup = RSUB; break; 1465 case CSUP: eSubSup = RSUP; break; 1466 default: 1467 break; 1468 } 1469 1470 // prevent sub-/supscripts from diminishing in size 1471 // (as would be in "a_{1_{2_{3_4}}}") 1472 if (GetFont().GetSize().Height() > rFormat.GetBaseSize().Height() / 3) 1473 { 1474 sal_uInt16 nIndex = (eSubSup == CSUB || eSubSup == CSUP) ? 1475 SIZ_LIMITS : SIZ_INDEX; 1476 Fraction aFraction ( rFormat.GetRelSize(nIndex), 100 ); 1477 pSubSup->SetSize(aFraction); 1478 } 1479 1480 pSubSup->Arrange(rDev, rFormat); 1481 1482 sal_Bool bIsTextmode = rFormat.IsTextmode(); 1483 nDist = 0; 1484 1485 //! be sure that CSUB, CSUP are handled before the other cases! 1486 switch (eSubSup) 1487 { case RSUB : 1488 case LSUB : 1489 if (!bIsTextmode) 1490 nDist = nOrigHeight 1491 * rFormat.GetDistance(DIS_SUBSCRIPT) / 100L; 1492 aPos = pSubSup->GetRect().AlignTo(aTmpRect, 1493 eSubSup == LSUB ? RP_LEFT : RP_RIGHT, 1494 RHA_CENTER, RVA_BOTTOM); 1495 aPos.Y() += nDist; 1496 nDelta = nDelimLine - aPos.Y(); 1497 if (nDelta > 0) 1498 aPos.Y() += nDelta; 1499 break; 1500 case RSUP : 1501 case LSUP : 1502 if (!bIsTextmode) 1503 nDist = nOrigHeight 1504 * rFormat.GetDistance(DIS_SUPERSCRIPT) / 100L; 1505 aPos = pSubSup->GetRect().AlignTo(aTmpRect, 1506 eSubSup == LSUP ? RP_LEFT : RP_RIGHT, 1507 RHA_CENTER, RVA_TOP); 1508 aPos.Y() -= nDist; 1509 nDelta = aPos.Y() + pSubSup->GetHeight() - nDelimLine; 1510 if (nDelta > 0) 1511 aPos.Y() -= nDelta; 1512 break; 1513 case CSUB : 1514 if (!bIsTextmode) 1515 nDist = nOrigHeight 1516 * rFormat.GetDistance(DIS_LOWERLIMIT) / 100L; 1517 aPos = pSubSup->GetRect().AlignTo(rBodyRect, RP_BOTTOM, 1518 RHA_CENTER, RVA_BASELINE); 1519 aPos.Y() += nDist; 1520 break; 1521 case CSUP : 1522 if (!bIsTextmode) 1523 nDist = nOrigHeight 1524 * rFormat.GetDistance(DIS_UPPERLIMIT) / 100L; 1525 aPos = pSubSup->GetRect().AlignTo(rBodyRect, RP_TOP, 1526 RHA_CENTER, RVA_BASELINE); 1527 aPos.Y() -= nDist; 1528 break; 1529 default : 1530 DBG_ASSERT(sal_False, "Sm: unbekannter Fall"); 1531 break; 1532 } 1533 1534 pSubSup->MoveTo(aPos); 1535 ExtendBy(*pSubSup, RCP_THIS, (sal_Bool) sal_True); 1536 1537 // update rectangle to which RSUB, RSUP, LSUB, LSUP 1538 // will be aligned to 1539 if (eSubSup == CSUB || eSubSup == CSUP) 1540 aTmpRect = *this; 1541 } 1542 } 1543 1544 void SmSubSupNode::CreateTextFromNode(String &rText) 1545 { 1546 SmNode *pNode; 1547 GetSubNode(0)->CreateTextFromNode(rText); 1548 1549 if (NULL != (pNode = GetSubNode(LSUB+1))) 1550 { 1551 APPEND(rText,"lsub "); 1552 pNode->CreateTextFromNode(rText); 1553 } 1554 if (NULL != (pNode = GetSubNode(LSUP+1))) 1555 { 1556 APPEND(rText,"lsup "); 1557 pNode->CreateTextFromNode(rText); 1558 } 1559 if (NULL != (pNode = GetSubNode(CSUB+1))) 1560 { 1561 APPEND(rText,"csub "); 1562 pNode->CreateTextFromNode(rText); 1563 } 1564 if (NULL != (pNode = GetSubNode(CSUP+1))) 1565 { 1566 APPEND(rText,"csup "); 1567 pNode->CreateTextFromNode(rText); 1568 } 1569 if (NULL != (pNode = GetSubNode(RSUB+1))) 1570 { 1571 rText.EraseTrailingChars(); 1572 rText.Append('_'); 1573 pNode->CreateTextFromNode(rText); 1574 } 1575 if (NULL != (pNode = GetSubNode(RSUP+1))) 1576 { 1577 rText.EraseTrailingChars(); 1578 rText.Append('^'); 1579 pNode->CreateTextFromNode(rText); 1580 } 1581 } 1582 1583 1584 /**************************************************************************/ 1585 1586 void SmBraceNode::CreateTextFromNode(String &rText) 1587 { 1588 if (GetScaleMode() == SCALE_HEIGHT) 1589 APPEND(rText,"left "); 1590 { 1591 String aStr; 1592 GetSubNode(0)->CreateTextFromNode(aStr); 1593 aStr.EraseLeadingAndTrailingChars(); 1594 aStr.EraseLeadingChars('\\'); 1595 if (aStr.Len()) 1596 { 1597 if (aStr.EqualsAscii("divides")) 1598 APPEND(rText,"lline"); 1599 else if (aStr.EqualsAscii("parallel")) 1600 APPEND(rText,"ldline"); 1601 else if (aStr.EqualsAscii("<")) 1602 APPEND(rText,"langle"); 1603 else 1604 rText.Append(aStr); 1605 rText.Append(' '); 1606 } 1607 else 1608 APPEND(rText,"none "); 1609 } 1610 GetSubNode(1)->CreateTextFromNode(rText); 1611 if (GetScaleMode() == SCALE_HEIGHT) 1612 APPEND(rText,"right "); 1613 { 1614 String aStr; 1615 GetSubNode(2)->CreateTextFromNode(aStr); 1616 aStr.EraseLeadingAndTrailingChars(); 1617 aStr.EraseLeadingChars('\\'); 1618 if (aStr.Len()) 1619 { 1620 if (aStr.EqualsAscii("divides")) 1621 APPEND(rText,"rline"); 1622 else if (aStr.EqualsAscii("parallel")) 1623 APPEND(rText,"rdline"); 1624 else if (aStr.EqualsAscii(">")) 1625 APPEND(rText,"rangle"); 1626 else 1627 rText.Append(aStr); 1628 rText.Append(' '); 1629 } 1630 else 1631 APPEND(rText,"none "); 1632 } 1633 rText.Append(' '); 1634 1635 } 1636 1637 void SmBraceNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) 1638 { 1639 SmNode *pLeft = GetSubNode(0), 1640 *pBody = GetSubNode(1), 1641 *pRight = GetSubNode(2); 1642 DBG_ASSERT(pLeft, "Sm: NULL pointer"); 1643 DBG_ASSERT(pBody, "Sm: NULL pointer"); 1644 DBG_ASSERT(pRight, "Sm: NULL pointer"); 1645 1646 pBody->Arrange(rDev, rFormat); 1647 1648 sal_Bool bIsScaleNormal = rFormat.IsScaleNormalBrackets(), 1649 bScale = pBody->GetHeight() > 0 && 1650 (GetScaleMode() == SCALE_HEIGHT || bIsScaleNormal), 1651 bIsABS = GetToken().eType == TABS; 1652 1653 long nFaceHeight = GetFont().GetSize().Height(); 1654 1655 // Uebergroesse in % ermitteln 1656 sal_uInt16 nPerc = 0; 1657 if (!bIsABS && bScale) 1658 { // im Fall von Klammern mit Uebergroesse... 1659 sal_uInt16 nIndex = GetScaleMode() == SCALE_HEIGHT ? 1660 DIS_BRACKETSIZE : DIS_NORMALBRACKETSIZE; 1661 nPerc = rFormat.GetDistance(nIndex); 1662 } 1663 1664 // ermitteln der Hoehe fuer die Klammern 1665 long nBraceHeight; 1666 if (bScale) 1667 { 1668 nBraceHeight = pBody->GetType() == NBRACEBODY ? 1669 ((SmBracebodyNode *) pBody)->GetBodyHeight() 1670 : pBody->GetHeight(); 1671 nBraceHeight += 2 * (nBraceHeight * nPerc / 100L); 1672 } 1673 else 1674 nBraceHeight = nFaceHeight; 1675 1676 // Abstand zum Argument 1677 nPerc = bIsABS ? 0 : rFormat.GetDistance(DIS_BRACKETSPACE); 1678 long nDist = nFaceHeight * nPerc / 100L; 1679 1680 // sofern erwuenscht skalieren der Klammern auf die gewuenschte Groesse 1681 if (bScale) 1682 { 1683 Size aTmpSize (pLeft->GetFont().GetSize()); 1684 DBG_ASSERT(pRight->GetFont().GetSize() == aTmpSize, 1685 "Sm : unterschiedliche Fontgroessen"); 1686 aTmpSize.Width() = Min((long) nBraceHeight * 60L / 100L, 1687 rFormat.GetBaseSize().Height() * 3L / 2L); 1688 // correction factor since change from StarMath to OpenSymbol font 1689 // because of the different font width in the FontMetric 1690 aTmpSize.Width() *= 182; 1691 aTmpSize.Width() /= 267; 1692 1693 xub_Unicode cChar = pLeft->GetToken().cMathChar; 1694 if (cChar != MS_LINE && cChar != MS_DLINE) 1695 pLeft ->GetFont().SetSize(aTmpSize); 1696 1697 cChar = pRight->GetToken().cMathChar; 1698 if (cChar != MS_LINE && cChar != MS_DLINE) 1699 pRight->GetFont().SetSize(aTmpSize); 1700 1701 pLeft ->AdaptToY(rDev, nBraceHeight); 1702 pRight->AdaptToY(rDev, nBraceHeight); 1703 } 1704 1705 pLeft ->Arrange(rDev, rFormat); 1706 pRight->Arrange(rDev, rFormat); 1707 1708 // damit auch "\(a\) - (a) - left ( a right )" vernuenftig aussieht 1709 RectVerAlign eVerAlign = bScale ? RVA_CENTERY : RVA_BASELINE; 1710 1711 Point aPos; 1712 aPos = pLeft->AlignTo(*pBody, RP_LEFT, RHA_CENTER, eVerAlign); 1713 aPos.X() -= nDist; 1714 pLeft->MoveTo(aPos); 1715 1716 aPos = pRight->AlignTo(*pBody, RP_RIGHT, RHA_CENTER, eVerAlign); 1717 aPos.X() += nDist; 1718 pRight->MoveTo(aPos); 1719 1720 SmRect::operator = (*pBody); 1721 ExtendBy(*pLeft, RCP_THIS).ExtendBy(*pRight, RCP_THIS); 1722 } 1723 1724 1725 /**************************************************************************/ 1726 1727 1728 void SmBracebodyNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) 1729 { 1730 sal_uInt16 nNumSubNodes = GetNumSubNodes(); 1731 if (nNumSubNodes == 0) 1732 return; 1733 1734 // arrange arguments 1735 sal_uInt16 i; 1736 for (i = 0; i < nNumSubNodes; i += 2) 1737 GetSubNode(i)->Arrange(rDev, rFormat); 1738 1739 // build reference rectangle with necessary info for vertical alignment 1740 SmRect aRefRect (*GetSubNode(0)); 1741 for (i = 0; i < nNumSubNodes; i += 2) 1742 { 1743 SmRect aTmpRect (*GetSubNode(i)); 1744 Point aPos = aTmpRect.AlignTo(aRefRect, RP_RIGHT, RHA_CENTER, RVA_BASELINE); 1745 aTmpRect.MoveTo(aPos); 1746 aRefRect.ExtendBy(aTmpRect, RCP_XOR); 1747 } 1748 1749 nBodyHeight = aRefRect.GetHeight(); 1750 1751 // scale separators to required height and arrange them 1752 sal_Bool bScale = GetScaleMode() == SCALE_HEIGHT || rFormat.IsScaleNormalBrackets(); 1753 long nHeight = bScale ? aRefRect.GetHeight() : GetFont().GetSize().Height(); 1754 sal_uInt16 nIndex = GetScaleMode() == SCALE_HEIGHT ? 1755 DIS_BRACKETSIZE : DIS_NORMALBRACKETSIZE; 1756 sal_uInt16 nPerc = rFormat.GetDistance(nIndex); 1757 if (bScale) 1758 nHeight += 2 * (nHeight * nPerc / 100L); 1759 for (i = 1; i < nNumSubNodes; i += 2) 1760 { 1761 SmNode *pNode = GetSubNode(i); 1762 pNode->AdaptToY(rDev, nHeight); 1763 pNode->Arrange(rDev, rFormat); 1764 } 1765 1766 // horizontal distance between argument and brackets or separators 1767 long nDist = GetFont().GetSize().Height() 1768 * rFormat.GetDistance(DIS_BRACKETSPACE) / 100L; 1769 1770 SmNode *pLeft = GetSubNode(0); 1771 SmRect::operator = (*pLeft); 1772 for (i = 1; i < nNumSubNodes; i++) 1773 { 1774 sal_Bool bIsSeparator = i % 2 != 0; 1775 RectVerAlign eVerAlign = bIsSeparator ? RVA_CENTERY : RVA_BASELINE; 1776 1777 SmNode *pRight = GetSubNode(i); 1778 Point aPosX = pRight->AlignTo(*pLeft, RP_RIGHT, RHA_CENTER, eVerAlign), 1779 aPosY = pRight->AlignTo(aRefRect, RP_RIGHT, RHA_CENTER, eVerAlign); 1780 aPosX.X() += nDist; 1781 1782 pRight->MoveTo(Point(aPosX.X(), aPosY.Y())); 1783 ExtendBy(*pRight, bIsSeparator ? RCP_THIS : RCP_XOR); 1784 1785 pLeft = pRight; 1786 } 1787 } 1788 1789 1790 /**************************************************************************/ 1791 1792 1793 void SmVerticalBraceNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) 1794 { 1795 SmNode *pBody = GetSubNode(0), 1796 *pBrace = GetSubNode(1), 1797 *pScript = GetSubNode(2); 1798 DBG_ASSERT(pBody, "Sm: NULL pointer!"); 1799 DBG_ASSERT(pBrace, "Sm: NULL pointer!"); 1800 DBG_ASSERT(pScript, "Sm: NULL pointer!"); 1801 1802 SmTmpDevice aTmpDev ((OutputDevice &) rDev, sal_True); 1803 aTmpDev.SetFont(GetFont()); 1804 1805 pBody->Arrange(aTmpDev, rFormat); 1806 1807 // Groesse wie bei Grenzen fuer diesen Teil 1808 pScript->SetSize( Fraction( rFormat.GetRelSize(SIZ_LIMITS), 100 ) ); 1809 // etwas hoehere Klammern als normal 1810 pBrace ->SetSize( Fraction(3, 2) ); 1811 1812 long nItalicWidth = pBody->GetItalicWidth(); 1813 if (nItalicWidth > 0) 1814 pBrace->AdaptToX(aTmpDev, nItalicWidth); 1815 1816 pBrace ->Arrange(aTmpDev, rFormat); 1817 pScript->Arrange(aTmpDev, rFormat); 1818 1819 // die relativen Position und die Abstaende zueinander bestimmen 1820 RectPos eRectPos; 1821 long nFontHeight = pBody->GetFont().GetSize().Height(); 1822 long nDistBody = nFontHeight * rFormat.GetDistance(DIS_ORNAMENTSIZE), 1823 nDistScript = nFontHeight; 1824 if (GetToken().eType == TOVERBRACE) 1825 { 1826 eRectPos = RP_TOP; 1827 nDistBody = - nDistBody; 1828 nDistScript *= - rFormat.GetDistance(DIS_UPPERLIMIT); 1829 } 1830 else // TUNDERBRACE 1831 { 1832 eRectPos = RP_BOTTOM; 1833 nDistScript *= + rFormat.GetDistance(DIS_LOWERLIMIT); 1834 } 1835 nDistBody /= 100L; 1836 nDistScript /= 100L; 1837 1838 Point aPos = pBrace->AlignTo(*pBody, eRectPos, RHA_CENTER, RVA_BASELINE); 1839 aPos.Y() += nDistBody; 1840 pBrace->MoveTo(aPos); 1841 1842 aPos = pScript->AlignTo(*pBrace, eRectPos, RHA_CENTER, RVA_BASELINE); 1843 aPos.Y() += nDistScript; 1844 pScript->MoveTo(aPos); 1845 1846 SmRect::operator = (*pBody); 1847 ExtendBy(*pBrace, RCP_THIS).ExtendBy(*pScript, RCP_THIS); 1848 } 1849 1850 1851 /**************************************************************************/ 1852 1853 1854 SmNode * SmOperNode::GetSymbol() 1855 { 1856 SmNode *pNode = GetSubNode(0); 1857 DBG_ASSERT(pNode, "Sm: NULL pointer!"); 1858 1859 if (pNode->GetType() == NSUBSUP) 1860 pNode = ((SmSubSupNode *) pNode)->GetBody(); 1861 1862 DBG_ASSERT(pNode, "Sm: NULL pointer!"); 1863 return pNode; 1864 } 1865 1866 1867 long SmOperNode::CalcSymbolHeight(const SmNode &rSymbol, 1868 const SmFormat &rFormat) const 1869 // returns the font height to be used for operator-symbol 1870 { 1871 long nHeight = GetFont().GetSize().Height(); 1872 1873 SmTokenType eTmpType = GetToken().eType; 1874 if (eTmpType == TLIM || eTmpType == TLIMINF || eTmpType == TLIMSUP) 1875 return nHeight; 1876 1877 if (!rFormat.IsTextmode()) 1878 { 1879 // set minimum size () 1880 nHeight += (nHeight * 20L) / 100L; 1881 1882 nHeight += nHeight 1883 * rFormat.GetDistance(DIS_OPERATORSIZE) / 100L; 1884 nHeight = nHeight * 686L / 845L; 1885 } 1886 1887 // correct user-defined symbols to match height of sum from used font 1888 if (rSymbol.GetToken().eType == TSPECIAL) 1889 nHeight = nHeight * 845L / 686L; 1890 1891 return nHeight; 1892 } 1893 1894 1895 void SmOperNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) 1896 { 1897 SmNode *pOper = GetSubNode(0); 1898 SmNode *pBody = GetSubNode(1); 1899 1900 DBG_ASSERT(pOper, "Sm: Subnode fehlt"); 1901 DBG_ASSERT(pBody, "Sm: Subnode fehlt"); 1902 1903 SmNode *pSymbol = GetSymbol(); 1904 pSymbol->SetSize(Fraction(CalcSymbolHeight(*pSymbol, rFormat), 1905 pSymbol->GetFont().GetSize().Height())); 1906 1907 pBody->Arrange(rDev, rFormat); 1908 pOper->Arrange(rDev, rFormat); 1909 1910 long nOrigHeight = GetFont().GetSize().Height(), 1911 nDist = nOrigHeight 1912 * rFormat.GetDistance(DIS_OPERATORSPACE) / 100L; 1913 1914 Point aPos = pOper->AlignTo(*pBody, RP_LEFT, RHA_CENTER, /*RVA_CENTERY*/RVA_MID); 1915 aPos.X() -= nDist; 1916 pOper->MoveTo(aPos); 1917 1918 SmRect::operator = (*pBody); 1919 ExtendBy(*pOper, RCP_THIS); 1920 } 1921 1922 1923 /**************************************************************************/ 1924 1925 1926 void SmAlignNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) 1927 // setzt im ganzen subtree (incl aktuellem node) das alignment 1928 { 1929 DBG_ASSERT(GetNumSubNodes() > 0, "Sm: SubNode fehlt"); 1930 1931 SmNode *pNode = GetSubNode(0); 1932 1933 RectHorAlign eHorAlign = RHA_CENTER; 1934 switch (GetToken().eType) 1935 { 1936 case TALIGNL: eHorAlign = RHA_LEFT; break; 1937 case TALIGNC: eHorAlign = RHA_CENTER; break; 1938 case TALIGNR: eHorAlign = RHA_RIGHT; break; 1939 default: 1940 break; 1941 } 1942 SetRectHorAlign(eHorAlign); 1943 1944 pNode->Arrange(rDev, rFormat); 1945 1946 SmRect::operator = (pNode->GetRect()); 1947 } 1948 1949 1950 /**************************************************************************/ 1951 1952 1953 void SmAttributNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) 1954 { 1955 SmNode *pAttr = GetSubNode(0), 1956 *pBody = GetSubNode(1); 1957 DBG_ASSERT(pBody, "Sm: Body fehlt"); 1958 DBG_ASSERT(pAttr, "Sm: Attribut fehlt"); 1959 1960 pBody->Arrange(rDev, rFormat); 1961 1962 if (GetScaleMode() == SCALE_WIDTH) 1963 pAttr->AdaptToX(rDev, pBody->GetItalicWidth()); 1964 pAttr->Arrange(rDev, rFormat); 1965 1966 // get relative position of attribut 1967 RectVerAlign eVerAlign; 1968 long nDist = 0; 1969 switch (GetToken().eType) 1970 { case TUNDERLINE : 1971 eVerAlign = RVA_ATTRIBUT_LO; 1972 break; 1973 case TOVERSTRIKE : 1974 eVerAlign = RVA_ATTRIBUT_MID; 1975 break; 1976 default : 1977 eVerAlign = RVA_ATTRIBUT_HI; 1978 if (pBody->GetType() == NATTRIBUT) 1979 nDist = GetFont().GetSize().Height() 1980 * rFormat.GetDistance(DIS_ORNAMENTSPACE) / 100L; 1981 } 1982 Point aPos = pAttr->AlignTo(*pBody, RP_ATTRIBUT, RHA_CENTER, eVerAlign); 1983 aPos.Y() -= nDist; 1984 pAttr->MoveTo(aPos); 1985 1986 SmRect::operator = (*pBody); 1987 ExtendBy(*pAttr, RCP_THIS, (sal_Bool) sal_True); 1988 } 1989 1990 1991 /**************************************************************************/ 1992 1993 1994 1995 1996 void SmFontNode::CreateTextFromNode(String &rText) 1997 { 1998 switch (GetToken().eType) 1999 { 2000 case TBOLD: 2001 APPEND(rText,"bold "); 2002 break; 2003 case TNBOLD: 2004 APPEND(rText,"nbold "); 2005 break; 2006 case TITALIC: 2007 APPEND(rText,"italic "); 2008 break; 2009 case TNITALIC: 2010 APPEND(rText,"nitalic "); 2011 break; 2012 case TPHANTOM: 2013 APPEND(rText,"phantom "); 2014 break; 2015 case TSIZE: 2016 { 2017 APPEND(rText,"size "); 2018 switch (nSizeType) 2019 { 2020 case FNTSIZ_PLUS: 2021 rText.Append('+'); 2022 break; 2023 case FNTSIZ_MINUS: 2024 rText.Append('-'); 2025 break; 2026 case FNTSIZ_MULTIPLY: 2027 rText.Append('*'); 2028 break; 2029 case FNTSIZ_DIVIDE: 2030 rText.Append('/'); 2031 break; 2032 case FNTSIZ_ABSOLUT: 2033 default: 2034 break; 2035 } 2036 rText += String( ::rtl::math::doubleToUString( 2037 static_cast<double>(aFontSize), 2038 rtl_math_StringFormat_Automatic, 2039 rtl_math_DecimalPlaces_Max, '.', sal_True)); 2040 rText.Append(' '); 2041 } 2042 break; 2043 case TBLACK: 2044 APPEND(rText,"color black "); 2045 break; 2046 case TWHITE: 2047 APPEND(rText,"color white "); 2048 break; 2049 case TRED: 2050 APPEND(rText,"color red "); 2051 break; 2052 case TGREEN: 2053 APPEND(rText,"color green "); 2054 break; 2055 case TBLUE: 2056 APPEND(rText,"color blue "); 2057 break; 2058 case TCYAN: 2059 APPEND(rText,"color cyan "); 2060 break; 2061 case TMAGENTA: 2062 APPEND(rText,"color magenta "); 2063 break; 2064 case TYELLOW: 2065 APPEND(rText,"color yellow "); 2066 break; 2067 case TSANS: 2068 APPEND(rText,"font sans "); 2069 break; 2070 case TSERIF: 2071 APPEND(rText,"font serif "); 2072 break; 2073 case TFIXED: 2074 APPEND(rText,"font fixed "); 2075 break; 2076 default: 2077 break; 2078 } 2079 GetSubNode(1)->CreateTextFromNode(rText); 2080 } 2081 2082 2083 void SmFontNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell) 2084 { 2085 //! prepare subnodes first 2086 SmNode::Prepare(rFormat, rDocShell); 2087 2088 int nFnt = -1; 2089 switch (GetToken().eType) 2090 { 2091 case TFIXED: nFnt = FNT_FIXED; break; 2092 case TSANS: nFnt = FNT_SANS; break; 2093 case TSERIF: nFnt = FNT_SERIF; break; 2094 default: 2095 break; 2096 } 2097 if (nFnt != -1) 2098 { GetFont() = rFormat.GetFont( sal::static_int_cast< sal_uInt16 >(nFnt) ); 2099 SetFont(GetFont()); 2100 } 2101 2102 //! prevent overwrites of this font by 'Arrange' or 'SetFont' calls of 2103 //! other font nodes (those with lower depth in the tree) 2104 Flags() |= FLG_FONT; 2105 } 2106 2107 2108 void SmFontNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) 2109 { 2110 SmNode *pNode = GetSubNode(1); 2111 DBG_ASSERT(pNode, "Sm: SubNode fehlt"); 2112 2113 switch (GetToken().eType) 2114 { case TSIZE : 2115 pNode->SetFontSize(aFontSize, nSizeType); 2116 break; 2117 case TSANS : 2118 case TSERIF : 2119 case TFIXED : 2120 pNode->SetFont(GetFont()); 2121 break; 2122 case TUNKNOWN : break; // no assertion on "font <?> <?>" 2123 2124 case TPHANTOM : SetPhantom(sal_True); break; 2125 case TBOLD : SetAttribut(ATTR_BOLD); break; 2126 case TITALIC : SetAttribut(ATTR_ITALIC); break; 2127 case TNBOLD : ClearAttribut(ATTR_BOLD); break; 2128 case TNITALIC : ClearAttribut(ATTR_ITALIC); break; 2129 2130 case TBLACK : SetColor(Color(COL_BLACK)); break; 2131 case TWHITE : SetColor(Color(COL_WHITE)); break; 2132 case TRED : SetColor(Color(COL_RED)); break; 2133 case TGREEN : SetColor(Color(COL_GREEN)); break; 2134 case TBLUE : SetColor(Color(COL_BLUE)); break; 2135 case TCYAN : SetColor(Color(COL_CYAN)); break; 2136 case TMAGENTA : SetColor(Color(COL_MAGENTA)); break; 2137 case TYELLOW : SetColor(Color(COL_YELLOW)); break; 2138 2139 default: 2140 DBG_ASSERT(sal_False, "Sm: unbekannter Fall"); 2141 } 2142 2143 pNode->Arrange(rDev, rFormat); 2144 2145 SmRect::operator = (pNode->GetRect()); 2146 } 2147 2148 2149 void SmFontNode::SetSizeParameter(const Fraction& rValue, sal_uInt16 Type) 2150 { 2151 nSizeType = Type; 2152 aFontSize = rValue; 2153 } 2154 2155 2156 /**************************************************************************/ 2157 2158 2159 SmPolyLineNode::SmPolyLineNode(const SmToken &rNodeToken) 2160 : SmGraphicNode(NPOLYLINE, rNodeToken) 2161 { 2162 aPoly.SetSize(2); 2163 nWidth = 0; 2164 } 2165 2166 2167 void SmPolyLineNode::AdaptToX(const OutputDevice &/*rDev*/, sal_uLong nNewWidth) 2168 { 2169 aToSize.Width() = nNewWidth; 2170 } 2171 2172 2173 void SmPolyLineNode::AdaptToY(const OutputDevice &/*rDev*/, sal_uLong nNewHeight) 2174 { 2175 GetFont().FreezeBorderWidth(); 2176 aToSize.Height() = nNewHeight; 2177 } 2178 2179 2180 void SmPolyLineNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) 2181 { 2182 //! some routines being called extract some info from the OutputDevice's 2183 //! font (eg the space to be used for borders OR the font name(!!)). 2184 //! Thus the font should reflect the needs and has to be set! 2185 SmTmpDevice aTmpDev ((OutputDevice &) rDev, sal_True); 2186 aTmpDev.SetFont(GetFont()); 2187 2188 long nBorderwidth = GetFont().GetBorderWidth(); 2189 2190 // 2191 // Das Polygon mit den beiden Endpunkten bilden 2192 // 2193 DBG_ASSERT(aPoly.GetSize() == 2, "Sm : falsche Anzahl von Punkten"); 2194 Point aPointA, aPointB; 2195 if (GetToken().eType == TWIDESLASH) 2196 { 2197 aPointA.X() = nBorderwidth; 2198 aPointA.Y() = aToSize.Height() - nBorderwidth; 2199 aPointB.X() = aToSize.Width() - nBorderwidth; 2200 aPointB.Y() = nBorderwidth; 2201 } 2202 else 2203 { 2204 DBG_ASSERT(GetToken().eType == TWIDEBACKSLASH, "Sm : unerwartetes Token"); 2205 aPointA.X() = 2206 aPointA.Y() = nBorderwidth; 2207 aPointB.X() = aToSize.Width() - nBorderwidth; 2208 aPointB.Y() = aToSize.Height() - nBorderwidth; 2209 } 2210 aPoly.SetPoint(aPointA, 0); 2211 aPoly.SetPoint(aPointB, 1); 2212 2213 long nThick = GetFont().GetSize().Height() 2214 * rFormat.GetDistance(DIS_STROKEWIDTH) / 100L; 2215 nWidth = nThick + 2 * nBorderwidth; 2216 2217 SmRect::operator = (SmRect(aToSize.Width(), aToSize.Height())); 2218 } 2219 2220 2221 void SmPolyLineNode::Draw(OutputDevice &rDev, const Point &rPosition) const 2222 { 2223 if (IsPhantom()) 2224 return; 2225 2226 long nBorderwidth = GetFont().GetBorderWidth(); 2227 2228 LineInfo aInfo; 2229 aInfo.SetWidth(nWidth - 2 * nBorderwidth); 2230 2231 Point aOffset (Point() - aPoly.GetBoundRect().TopLeft() 2232 + Point(nBorderwidth, nBorderwidth)), 2233 aPos (rPosition + aOffset); 2234 ((Polygon &) aPoly).Move(aPos.X(), aPos.Y()); 2235 2236 SmTmpDevice aTmpDev ((OutputDevice &) rDev, sal_False); 2237 aTmpDev.SetLineColor( GetFont().GetColor() ); 2238 2239 rDev.DrawPolyLine(aPoly, aInfo); 2240 2241 #ifdef SM_RECT_DEBUG 2242 if (!IsDebug()) 2243 return; 2244 2245 int nRFlags = SM_RECT_CORE | SM_RECT_ITALIC | SM_RECT_LINES | SM_RECT_MID; 2246 SmRect::Draw(rDev, rPosition, nRFlags); 2247 #endif 2248 } 2249 2250 2251 /**************************************************************************/ 2252 2253 void SmRootSymbolNode::AdaptToX(const OutputDevice &/*rDev*/, sal_uLong nWidth) 2254 { 2255 nBodyWidth = nWidth; 2256 } 2257 2258 2259 void SmRootSymbolNode::AdaptToY(const OutputDevice &rDev, sal_uLong nHeight) 2260 { 2261 // etwas extra Laenge damit der horizontale Balken spaeter ueber dem 2262 // Argument positioniert ist 2263 SmMathSymbolNode::AdaptToY(rDev, nHeight + nHeight / 10L); 2264 } 2265 2266 2267 void SmRootSymbolNode::Draw(OutputDevice &rDev, const Point &rPosition) const 2268 { 2269 if (IsPhantom()) 2270 return; 2271 2272 // draw root-sign itself 2273 SmMathSymbolNode::Draw(rDev, rPosition); 2274 2275 SmTmpDevice aTmpDev( (OutputDevice &) rDev, sal_True ); 2276 aTmpDev.SetFillColor(GetFont().GetColor()); 2277 rDev.SetLineColor(); 2278 aTmpDev.SetFont( GetFont() ); 2279 2280 // since the width is always unscaled it corresponds ot the _original_ 2281 // _unscaled_ font height to be used, we use that to calculate the 2282 // bar height. Thus it is independent of the arguments height. 2283 // ( see display of sqrt QQQ versus sqrt stack{Q#Q#Q#Q} ) 2284 long nBarHeight = GetWidth() * 7L / 100L; 2285 long nBarWidth = nBodyWidth + GetBorderWidth(); 2286 Point aBarOffset( GetWidth(), +GetBorderWidth() ); 2287 Point aBarPos( rPosition + aBarOffset ); 2288 2289 Rectangle aBar(aBarPos, Size( nBarWidth, nBarHeight) ); 2290 //! avoid GROWING AND SHRINKING of drawn rectangle when constantly 2291 //! increasing zoomfactor. 2292 // This is done by shifting it's output-position to a point that 2293 // corresponds exactly to a pixel on the output device. 2294 Point aDrawPos( rDev.PixelToLogic(rDev.LogicToPixel(aBar.TopLeft())) ); 2295 //aDrawPos.X() = aBar.Left(); //! don't change X position 2296 aBar.SetPos( aDrawPos ); 2297 2298 rDev.DrawRect( aBar ); 2299 2300 #ifdef SM_RECT_DEBUG 2301 if (!IsDebug()) 2302 return; 2303 2304 int nRFlags = SM_RECT_CORE | SM_RECT_ITALIC | SM_RECT_LINES | SM_RECT_MID; 2305 SmRect::Draw(rDev, rPosition, nRFlags); 2306 #endif 2307 } 2308 2309 2310 /**************************************************************************/ 2311 2312 2313 void SmRectangleNode::AdaptToX(const OutputDevice &/*rDev*/, sal_uLong nWidth) 2314 { 2315 aToSize.Width() = nWidth; 2316 } 2317 2318 2319 void SmRectangleNode::AdaptToY(const OutputDevice &/*rDev*/, sal_uLong nHeight) 2320 { 2321 GetFont().FreezeBorderWidth(); 2322 aToSize.Height() = nHeight; 2323 } 2324 2325 2326 void SmRectangleNode::Arrange(const OutputDevice &rDev, const SmFormat &/*rFormat*/) 2327 { 2328 long nFontHeight = GetFont().GetSize().Height(); 2329 long nWidth = aToSize.Width(), 2330 nHeight = aToSize.Height(); 2331 if (nHeight == 0) 2332 nHeight = nFontHeight / 30; 2333 if (nWidth == 0) 2334 nWidth = nFontHeight / 3; 2335 2336 SmTmpDevice aTmpDev ((OutputDevice &) rDev, sal_True); 2337 aTmpDev.SetFont(GetFont()); 2338 2339 // add some borderspace 2340 sal_uLong nTmpBorderWidth = GetFont().GetBorderWidth(); 2341 //nWidth += nTmpBorderWidth; 2342 nHeight += 2 * nTmpBorderWidth; 2343 2344 //! use this method in order to have 'SmRect::HasAlignInfo() == sal_True' 2345 //! and thus having the attribut-fences updated in 'SmRect::ExtendBy' 2346 SmRect::operator = (SmRect(nWidth, nHeight)); 2347 } 2348 2349 2350 void SmRectangleNode::Draw(OutputDevice &rDev, const Point &rPosition) const 2351 { 2352 if (IsPhantom()) 2353 return; 2354 2355 SmTmpDevice aTmpDev ((OutputDevice &) rDev, sal_False); 2356 aTmpDev.SetFillColor(GetFont().GetColor()); 2357 rDev.SetLineColor(); 2358 aTmpDev.SetFont(GetFont()); 2359 2360 sal_uLong nTmpBorderWidth = GetFont().GetBorderWidth(); 2361 2362 // get rectangle and remove borderspace 2363 Rectangle aTmp (AsRectangle() + rPosition - GetTopLeft()); 2364 aTmp.Left() += nTmpBorderWidth; 2365 aTmp.Right() -= nTmpBorderWidth; 2366 aTmp.Top() += nTmpBorderWidth; 2367 aTmp.Bottom() -= nTmpBorderWidth; 2368 2369 DBG_ASSERT(aTmp.GetHeight() > 0 && aTmp.GetWidth() > 0, 2370 "Sm: leeres Rechteck"); 2371 2372 //! avoid GROWING AND SHRINKING of drawn rectangle when constantly 2373 //! increasing zoomfactor. 2374 // This is done by shifting it's output-position to a point that 2375 // corresponds exactly to a pixel on the output device. 2376 Point aPos (rDev.PixelToLogic(rDev.LogicToPixel(aTmp.TopLeft()))); 2377 aTmp.SetPos(aPos); 2378 2379 rDev.DrawRect(aTmp); 2380 2381 #ifdef SM_RECT_DEBUG 2382 if (!IsDebug()) 2383 return; 2384 2385 int nRFlags = SM_RECT_CORE | SM_RECT_ITALIC | SM_RECT_LINES | SM_RECT_MID; 2386 SmRect::Draw(rDev, rPosition, nRFlags); 2387 #endif 2388 } 2389 2390 2391 /**************************************************************************/ 2392 2393 2394 SmTextNode::SmTextNode( SmNodeType eNodeType, const SmToken &rNodeToken, sal_uInt16 nFontDescP ) : 2395 SmVisibleNode(eNodeType, rNodeToken) 2396 { 2397 nFontDesc = nFontDescP; 2398 } 2399 2400 2401 SmTextNode::SmTextNode( const SmToken &rNodeToken, sal_uInt16 nFontDescP ) : 2402 SmVisibleNode(NTEXT, rNodeToken) 2403 { 2404 nFontDesc = nFontDescP; 2405 } 2406 2407 2408 void SmTextNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell) 2409 { 2410 SmNode::Prepare(rFormat, rDocShell); 2411 2412 // default setting for horizontal alignment of nodes with TTEXT 2413 // content is as alignl (cannot be done in Arrange since it would 2414 // override the settings made by an SmAlignNode before) 2415 if (TTEXT == GetToken().eType) 2416 SetRectHorAlign( RHA_LEFT ); 2417 2418 aText = GetToken().aText; 2419 GetFont() = rFormat.GetFont(GetFontDesc()); 2420 2421 if (IsItalic( GetFont() )) 2422 Attributes() |= ATTR_ITALIC; 2423 if (IsBold( GetFont() )) 2424 Attributes() |= ATTR_BOLD; 2425 2426 // special handling for ':' where it is a token on it's own and is likely 2427 // to be used for mathematical notations. (E.g. a:b = 2:3) 2428 // In that case it should not be displayed in italic. 2429 if (GetToken().aText.Len() == 1 && GetToken().aText.GetChar(0) == ':') 2430 Attributes() &= ~ATTR_ITALIC; 2431 }; 2432 2433 2434 void SmTextNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) 2435 { 2436 PrepareAttributes(); 2437 2438 sal_uInt16 nSizeDesc = GetFontDesc() == FNT_FUNCTION ? 2439 SIZ_FUNCTION : SIZ_TEXT; 2440 GetFont() *= Fraction (rFormat.GetRelSize(nSizeDesc), 100); 2441 2442 SmTmpDevice aTmpDev ((OutputDevice &) rDev, sal_True); 2443 aTmpDev.SetFont(GetFont()); 2444 2445 SmRect::operator = (SmRect(aTmpDev, &rFormat, aText, GetFont().GetBorderWidth())); 2446 } 2447 2448 void SmTextNode::CreateTextFromNode(String &rText) 2449 { 2450 sal_Bool bQuoted=sal_False; 2451 if (GetToken().eType == TTEXT) 2452 { 2453 rText.Append('\"'); 2454 bQuoted=sal_True; 2455 } 2456 else 2457 { 2458 SmParser aParseTest; 2459 SmNode *pTable = aParseTest.Parse(GetToken().aText); 2460 bQuoted=sal_True; 2461 if ( (pTable->GetType() == NTABLE) && (pTable->GetNumSubNodes() == 1) ) 2462 { 2463 SmNode *pResult = pTable->GetSubNode(0); 2464 if ( (pResult->GetType() == NLINE) && 2465 (pResult->GetNumSubNodes() == 1) ) 2466 { 2467 pResult = pResult->GetSubNode(0); 2468 if ( (pResult->GetType() == NEXPRESSION) && 2469 (pResult->GetNumSubNodes() == 1) ) 2470 { 2471 pResult = pResult->GetSubNode(0); 2472 if (pResult->GetType() == NTEXT) 2473 bQuoted=sal_False; 2474 } 2475 } 2476 } 2477 delete pTable; 2478 2479 if ((GetToken().eType == TIDENT) && (GetFontDesc() == FNT_FUNCTION)) 2480 { 2481 //Search for existing functions and remove extraenous keyword 2482 APPEND(rText,"func "); 2483 } 2484 else if (bQuoted) 2485 APPEND(rText,"italic "); 2486 2487 if (bQuoted) 2488 rText.Append('\"'); 2489 2490 } 2491 2492 rText.Append(GetToken().aText); 2493 2494 if (bQuoted) 2495 rText.Append('\"'); 2496 rText.Append(' '); 2497 } 2498 2499 void SmTextNode::Draw(OutputDevice &rDev, const Point& rPosition) const 2500 { 2501 if (IsPhantom() || aText.Len() == 0 || aText.GetChar(0) == xub_Unicode('\0')) 2502 return; 2503 2504 SmTmpDevice aTmpDev ((OutputDevice &) rDev, sal_False); 2505 aTmpDev.SetFont(GetFont()); 2506 2507 Point aPos (rPosition); 2508 aPos.Y() += GetBaselineOffset(); 2509 // auf Pixelkoordinaten runden 2510 aPos = rDev.PixelToLogic( rDev.LogicToPixel(aPos) ); 2511 2512 #if OSL_DEBUG_LEVEL > 1 2513 sal_Int32 nPos = 0; 2514 sal_UCS4 cChar = OUString( aText ).iterateCodePoints( &nPos ); 2515 (void) cChar; 2516 #endif 2517 2518 rDev.DrawStretchText(aPos, GetWidth(), aText); 2519 2520 #ifdef SM_RECT_DEBUG 2521 if (!IsDebug()) 2522 return; 2523 2524 int nRFlags = SM_RECT_CORE | SM_RECT_ITALIC | SM_RECT_LINES | SM_RECT_MID; 2525 SmRect::Draw(rDev, rPosition, nRFlags); 2526 #endif 2527 } 2528 2529 void SmTextNode::GetAccessibleText( String &rText ) const 2530 { 2531 rText += aText; 2532 } 2533 2534 /**************************************************************************/ 2535 2536 void SmMatrixNode::CreateTextFromNode(String &rText) 2537 { 2538 APPEND(rText,"matrix {"); 2539 for (sal_uInt16 i = 0; i < nNumRows; i++) 2540 { 2541 for (sal_uInt16 j = 0; j < nNumCols; j++) 2542 { 2543 SmNode *pNode = GetSubNode(i * nNumCols + j); 2544 pNode->CreateTextFromNode(rText); 2545 if (j != nNumCols-1) 2546 APPEND(rText,"# "); 2547 } 2548 if (i != nNumRows-1) 2549 APPEND(rText,"## "); 2550 } 2551 rText.EraseTrailingChars(); 2552 APPEND(rText,"} "); 2553 } 2554 2555 2556 void SmMatrixNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) 2557 { 2558 Point aPosition, 2559 aOffset; 2560 SmNode *pNode; 2561 sal_uInt16 i, j; 2562 2563 // initialize array that is to hold the maximum widhts of all 2564 // elements (subnodes) in that column. 2565 long *pColWidth = new long[nNumCols]; 2566 for (j = 0; j < nNumCols; j++) 2567 pColWidth[j] = 0; 2568 2569 // arrange subnodes and calculate the aboves arrays contents 2570 sal_uInt16 nNodes = GetNumSubNodes(); 2571 for (i = 0; i < nNodes; i++) 2572 { 2573 sal_uInt16 nIdx = nNodes - 1 - i; 2574 if (NULL != (pNode = GetSubNode(nIdx))) 2575 { 2576 pNode->Arrange(rDev, rFormat); 2577 int nCol = nIdx % nNumCols; 2578 pColWidth[nCol] = Max(pColWidth[nCol], pNode->GetItalicWidth()); 2579 } 2580 } 2581 2582 // norm distance from which the following two are calcutated 2583 const int nNormDist = 3 * GetFont().GetSize().Height(); 2584 2585 // define horizontal and vertical minimal distances that seperate 2586 // the elements 2587 long nHorDist = nNormDist * rFormat.GetDistance(DIS_MATRIXCOL) / 100L, 2588 nVerDist = nNormDist * rFormat.GetDistance(DIS_MATRIXROW) / 100L; 2589 2590 // build array that holds the leftmost position for each column 2591 long *pColLeft = new long[nNumCols]; 2592 long nX = 0; 2593 for (j = 0; j < nNumCols; j++) 2594 { pColLeft[j] = nX; 2595 nX += pColWidth[j] + nHorDist; 2596 } 2597 2598 Point aPos, aDelta; 2599 SmRect aLineRect; 2600 SmRect::operator = (SmRect()); 2601 for (i = 0; i < nNumRows; i++) 2602 { aLineRect = SmRect(); 2603 for (j = 0; j < nNumCols; j++) 2604 { SmNode *pTmpNode = GetSubNode(i * nNumCols + j); 2605 DBG_ASSERT(pTmpNode, "Sm: NULL pointer"); 2606 2607 const SmRect &rNodeRect = pTmpNode->GetRect(); 2608 2609 // align all baselines in that row if possible 2610 aPos = rNodeRect.AlignTo(aLineRect, RP_RIGHT, RHA_CENTER, RVA_BASELINE); 2611 aPos.X() += nHorDist; 2612 2613 // get horizontal alignment 2614 const SmNode *pCoNode = pTmpNode->GetLeftMost(); 2615 RectHorAlign eHorAlign = pCoNode->GetRectHorAlign(); 2616 2617 // caculate horizontal position of element depending on column 2618 // and horizontal alignment 2619 switch (eHorAlign) 2620 { case RHA_LEFT: 2621 aPos.X() = rNodeRect.GetLeft() + pColLeft[j]; 2622 break; 2623 case RHA_CENTER: 2624 aPos.X() = rNodeRect.GetLeft() + pColLeft[j] 2625 + pColWidth[j] / 2 2626 - rNodeRect.GetItalicCenterX(); 2627 break; 2628 case RHA_RIGHT: 2629 aPos.X() = rNodeRect.GetLeft() + pColLeft[j] 2630 + pColWidth[j] - rNodeRect.GetItalicWidth(); 2631 break; 2632 } 2633 2634 pTmpNode->MoveTo(aPos); 2635 aLineRect.ExtendBy(rNodeRect, RCP_XOR); 2636 } 2637 2638 aPos = aLineRect.AlignTo(*this, RP_BOTTOM, RHA_CENTER, RVA_BASELINE); 2639 aPos.Y() += nVerDist; 2640 2641 // move 'aLineRect' and rectangles in that line to final position 2642 aDelta.X() = 0; // since horizontal alignment is already done 2643 aDelta.Y() = aPos.Y() - aLineRect.GetTop(); 2644 aLineRect.Move(aDelta); 2645 for (j = 0; j < nNumCols; j++) 2646 if (NULL != (pNode = GetSubNode(i * nNumCols + j))) 2647 pNode->Move(aDelta); 2648 2649 ExtendBy(aLineRect, RCP_NONE); 2650 } 2651 2652 delete [] pColLeft; 2653 delete [] pColWidth; 2654 } 2655 2656 2657 void SmMatrixNode::SetRowCol(sal_uInt16 nMatrixRows, sal_uInt16 nMatrixCols) 2658 { 2659 nNumRows = nMatrixRows; 2660 nNumCols = nMatrixCols; 2661 } 2662 2663 2664 SmNode * SmMatrixNode::GetLeftMost() 2665 { 2666 return this; 2667 } 2668 2669 2670 /**************************************************************************/ 2671 2672 2673 SmMathSymbolNode::SmMathSymbolNode(const SmToken &rNodeToken) 2674 : SmSpecialNode(NMATH, rNodeToken, FNT_MATH) 2675 { 2676 xub_Unicode cChar = GetToken().cMathChar; 2677 if ((xub_Unicode) '\0' != cChar) 2678 SetText( cChar ); 2679 } 2680 2681 void SmMathSymbolNode::AdaptToX(const OutputDevice &rDev, sal_uLong nWidth) 2682 { 2683 // Since there is no function to do this, we try to approximate it: 2684 Size aFntSize (GetFont().GetSize()); 2685 2686 //! however the result is a bit better with 'nWidth' as initial font width 2687 aFntSize.Width() = nWidth; 2688 GetFont().SetSize(aFntSize); 2689 2690 SmTmpDevice aTmpDev ((OutputDevice &) rDev, sal_True); 2691 aTmpDev.SetFont(GetFont()); 2692 2693 // get denominator of error factor for width 2694 long nTmpBorderWidth = GetFont().GetBorderWidth(); 2695 long nDenom = SmRect(aTmpDev, NULL, GetText(), nTmpBorderWidth).GetItalicWidth(); 2696 2697 // scale fontwidth with this error factor 2698 aFntSize.Width() *= nWidth; 2699 aFntSize.Width() /= nDenom ? nDenom : 1; 2700 2701 GetFont().SetSize(aFntSize); 2702 } 2703 2704 void SmMathSymbolNode::AdaptToY(const OutputDevice &rDev, sal_uLong nHeight) 2705 { 2706 GetFont().FreezeBorderWidth(); 2707 Size aFntSize (GetFont().GetSize()); 2708 2709 // da wir nur die Hoehe skalieren wollen muesen wir hier ggf die Fontweite 2710 // ermitteln um diese beizubehalten. 2711 if (aFntSize.Width() == 0) 2712 { 2713 OutputDevice &rDevNC = (OutputDevice &) rDev; 2714 rDevNC.Push(PUSH_FONT | PUSH_MAPMODE); 2715 rDevNC.SetFont(GetFont()); 2716 aFntSize.Width() = rDev.GetFontMetric().GetSize().Width(); 2717 rDevNC.Pop(); 2718 } 2719 DBG_ASSERT(aFntSize.Width() != 0, "Sm: "); 2720 2721 //! however the result is a bit better with 'nHeight' as initial 2722 //! font height 2723 aFntSize.Height() = nHeight; 2724 GetFont().SetSize(aFntSize); 2725 2726 SmTmpDevice aTmpDev ((OutputDevice &) rDev, sal_True); 2727 aTmpDev.SetFont(GetFont()); 2728 2729 // get denominator of error factor for height 2730 long nTmpBorderWidth = GetFont().GetBorderWidth(); 2731 long nDenom = SmRect(aTmpDev, NULL, GetText(), nTmpBorderWidth).GetHeight(); 2732 2733 // scale fontwidth with this error factor 2734 aFntSize.Height() *= nHeight; 2735 aFntSize.Height() /= nDenom ? nDenom : 1; 2736 2737 GetFont().SetSize(aFntSize); 2738 } 2739 2740 2741 void SmMathSymbolNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell) 2742 { 2743 SmNode::Prepare(rFormat, rDocShell); 2744 2745 GetFont() = rFormat.GetFont(GetFontDesc()); 2746 // use same font size as is used for variables 2747 GetFont().SetSize( rFormat.GetFont( FNT_VARIABLE ).GetSize() ); 2748 2749 DBG_ASSERT(GetFont().GetCharSet() == RTL_TEXTENCODING_SYMBOL || 2750 GetFont().GetCharSet() == RTL_TEXTENCODING_UNICODE, 2751 "incorrect charset for character from StarMath/OpenSymbol font"); 2752 2753 Flags() |= FLG_FONT | FLG_ITALIC; 2754 }; 2755 2756 2757 void SmMathSymbolNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) 2758 { 2759 const XubString &rText = GetText(); 2760 2761 if (rText.Len() == 0 || rText.GetChar(0) == xub_Unicode('\0')) 2762 { SmRect::operator = (SmRect()); 2763 return; 2764 } 2765 2766 PrepareAttributes(); 2767 2768 GetFont() *= Fraction (rFormat.GetRelSize(SIZ_TEXT), 100); 2769 2770 SmTmpDevice aTmpDev ((OutputDevice &) rDev, sal_True); 2771 aTmpDev.SetFont(GetFont()); 2772 2773 SmRect::operator = (SmRect(aTmpDev, &rFormat, rText, GetFont().GetBorderWidth())); 2774 } 2775 2776 void SmMathSymbolNode::CreateTextFromNode(String &rText) 2777 { 2778 String sStr; 2779 MathType::LookupChar(GetToken().cMathChar, sStr); 2780 rText.Append(sStr); 2781 } 2782 2783 void SmRectangleNode::CreateTextFromNode(String &rText) 2784 { 2785 switch (GetToken().eType) 2786 { 2787 case TUNDERLINE: 2788 APPEND(rText,"underline "); 2789 break; 2790 case TOVERLINE: 2791 APPEND(rText,"overline "); 2792 break; 2793 case TOVERSTRIKE: 2794 APPEND(rText,"overstrike "); 2795 break; 2796 default: 2797 break; 2798 } 2799 } 2800 2801 void SmAttributNode::CreateTextFromNode(String &rText) 2802 { 2803 SmNode *pNode; 2804 sal_uInt16 nSize = GetNumSubNodes(); 2805 DBG_ASSERT(nSize == 2, "Node missing members"); 2806 rText.Append('{'); 2807 sal_Unicode nLast=0; 2808 if (NULL != (pNode = GetSubNode(0))) 2809 { 2810 String aStr; 2811 pNode->CreateTextFromNode(aStr); 2812 if (aStr.Len() > 1) 2813 rText.Append(aStr); 2814 else 2815 { 2816 nLast = aStr.GetChar(0); 2817 switch (nLast) 2818 { 2819 case 0xAF: 2820 APPEND(rText,"overline "); 2821 break; 2822 case 0x2d9: 2823 APPEND(rText,"dot "); 2824 break; 2825 case 0x2dc: 2826 APPEND(rText,"widetilde "); 2827 break; 2828 case 0xA8: 2829 APPEND(rText,"ddot "); 2830 break; 2831 case 0xE082: 2832 break; 2833 case 0xE09B: 2834 APPEND(rText,"dddot "); 2835 break; 2836 default: 2837 rText.Append(nLast); 2838 break; 2839 } 2840 } 2841 } 2842 2843 if (nSize == 2) 2844 if (NULL != (pNode = GetSubNode(1))) 2845 pNode->CreateTextFromNode(rText); 2846 2847 rText.EraseTrailingChars(); 2848 2849 if (nLast == 0xE082) 2850 APPEND(rText," overbrace {}"); 2851 2852 APPEND(rText,"} "); 2853 } 2854 2855 /**************************************************************************/ 2856 2857 bool lcl_IsFromGreekSymbolSet( const String &rTokenText ) 2858 { 2859 bool bRes = false; 2860 2861 // valid symbol name needs to have a '%' at pos 0 and at least an additonal char 2862 if (rTokenText.Len() > 2 && rTokenText.GetBuffer()[0] == (sal_Unicode)'%') 2863 { 2864 String aName( rTokenText.Copy(1) ); 2865 SmSym *pSymbol = SM_MOD()->GetSymbolManager().GetSymbolByName( aName ); 2866 if (pSymbol && GetExportSymbolSetName( pSymbol->GetSymbolSetName() ).EqualsAscii( "Greek" ) ) 2867 bRes = true; 2868 } 2869 2870 return bRes; 2871 } 2872 2873 2874 SmSpecialNode::SmSpecialNode(SmNodeType eNodeType, const SmToken &rNodeToken, sal_uInt16 _nFontDesc) : 2875 SmTextNode(eNodeType, rNodeToken, _nFontDesc) 2876 { 2877 bIsFromGreekSymbolSet = lcl_IsFromGreekSymbolSet( rNodeToken.aText ); 2878 } 2879 2880 2881 SmSpecialNode::SmSpecialNode(const SmToken &rNodeToken) : 2882 SmTextNode(NSPECIAL, rNodeToken, FNT_MATH) //! default Font nicht immer richtig 2883 { 2884 bIsFromGreekSymbolSet = lcl_IsFromGreekSymbolSet( rNodeToken.aText ); 2885 } 2886 2887 2888 void SmSpecialNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell) 2889 { 2890 SmNode::Prepare(rFormat, rDocShell); 2891 2892 const SmSym *pSym; 2893 SmModule *pp = SM_MOD(); 2894 2895 String aName( GetToken().aText.Copy(1) ); 2896 if (NULL != (pSym = pp->GetSymbolManager().GetSymbolByName( aName ))) 2897 { 2898 sal_UCS4 cChar = pSym->GetCharacter(); 2899 String aTmp( OUString( &cChar, 1 ) ); 2900 SetText( aTmp ); 2901 GetFont() = pSym->GetFace(); 2902 } 2903 else 2904 { 2905 SetText( GetToken().aText ); 2906 GetFont() = rFormat.GetFont(FNT_VARIABLE); 2907 } 2908 // use same font size as is used for variables 2909 GetFont().SetSize( rFormat.GetFont( FNT_VARIABLE ).GetSize() ); 2910 2911 //! eigentlich sollten nur WEIGHT_NORMAL und WEIGHT_BOLD vorkommen... 2912 //! In der sms-Datei gibt es jedoch zB auch 'WEIGHT_ULTRALIGHT' 2913 //! daher vergleichen wir hier mit > statt mit != . 2914 //! (Langfristig sollte die Notwendigkeit fuer 'PrepareAttribut', und damit 2915 //! fuer dieses hier, mal entfallen.) 2916 // 2917 //! see also SmFontStyles::GetStyleName 2918 if (IsItalic( GetFont() )) 2919 SetAttribut(ATTR_ITALIC); 2920 if (IsBold( GetFont() )) 2921 SetAttribut(ATTR_BOLD); 2922 2923 Flags() |= FLG_FONT; 2924 2925 if (bIsFromGreekSymbolSet) 2926 { 2927 DBG_ASSERT( GetText().Len() == 1, "a symbol should only consist of 1 char!" ); 2928 bool bItalic = false; 2929 sal_Int16 nStyle = rFormat.GetGreekCharStyle(); 2930 DBG_ASSERT( nStyle >= 0 && nStyle <= 2, "unexpected value for GreekCharStyle" ); 2931 if (nStyle == 1) 2932 bItalic = true; 2933 else if (nStyle == 2) 2934 { 2935 String aTmp( GetText() ); 2936 if (aTmp.Len() > 0) 2937 { 2938 const sal_Unicode cUppercaseAlpha = 0x0391; 2939 const sal_Unicode cUppercaseOmega = 0x03A9; 2940 sal_Unicode cChar = aTmp.GetBuffer()[0]; 2941 // uppercase letters should be straight and lowercase letters italic 2942 bItalic = !(cUppercaseAlpha <= cChar && cChar <= cUppercaseOmega); 2943 } 2944 } 2945 2946 if (bItalic) 2947 Attributes() |= ATTR_ITALIC; 2948 else 2949 Attributes() &= ~ATTR_ITALIC;; 2950 } 2951 }; 2952 2953 2954 void SmSpecialNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) 2955 { 2956 PrepareAttributes(); 2957 2958 SmTmpDevice aTmpDev ((OutputDevice &) rDev, sal_True); 2959 aTmpDev.SetFont(GetFont()); 2960 2961 SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(), GetFont().GetBorderWidth())); 2962 } 2963 2964 2965 void SmSpecialNode::Draw(OutputDevice &rDev, const Point& rPosition) const 2966 { 2967 //! since this chars might come from any font, that we may not have 2968 //! set to ALIGN_BASELINE yet, we do it now. 2969 ((SmSpecialNode *)this)->GetFont().SetAlign(ALIGN_BASELINE); 2970 2971 SmTextNode::Draw(rDev, rPosition); 2972 } 2973 2974 2975 /**************************************************************************/ 2976 2977 2978 void SmGlyphSpecialNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) 2979 { 2980 PrepareAttributes(); 2981 2982 SmTmpDevice aTmpDev ((OutputDevice &) rDev, sal_True); 2983 aTmpDev.SetFont(GetFont()); 2984 2985 SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(), 2986 GetFont().GetBorderWidth()).AsGlyphRect()); 2987 } 2988 2989 2990 /**************************************************************************/ 2991 2992 2993 void SmPlaceNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell) 2994 { 2995 SmNode::Prepare(rFormat, rDocShell); 2996 2997 GetFont().SetColor(COL_GRAY); 2998 Flags() |= FLG_COLOR | FLG_FONT | FLG_ITALIC; 2999 }; 3000 3001 3002 void SmPlaceNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) 3003 { 3004 PrepareAttributes(); 3005 3006 SmTmpDevice aTmpDev ((OutputDevice &) rDev, sal_True); 3007 aTmpDev.SetFont(GetFont()); 3008 3009 SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(), GetFont().GetBorderWidth())); 3010 } 3011 3012 3013 /**************************************************************************/ 3014 3015 3016 void SmErrorNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell) 3017 { 3018 SmNode::Prepare(rFormat, rDocShell); 3019 3020 GetFont().SetColor(COL_RED); 3021 Flags() |= FLG_VISIBLE | FLG_BOLD | FLG_ITALIC 3022 | FLG_COLOR | FLG_FONT | FLG_SIZE; 3023 } 3024 3025 3026 void SmErrorNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) 3027 { 3028 PrepareAttributes(); 3029 3030 SmTmpDevice aTmpDev ((OutputDevice &) rDev, sal_True); 3031 aTmpDev.SetFont(GetFont()); 3032 3033 const XubString &rText = GetText(); 3034 SmRect::operator = (SmRect(aTmpDev, &rFormat, rText, GetFont().GetBorderWidth())); 3035 } 3036 3037 3038 /**************************************************************************/ 3039 3040 3041 void SmBlankNode::IncreaseBy(const SmToken &rToken) 3042 { 3043 switch(rToken.eType) 3044 { 3045 case TBLANK: nNum += 4; break; 3046 case TSBLANK: nNum += 1; break; 3047 default: 3048 break; 3049 } 3050 } 3051 3052 3053 void SmBlankNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell) 3054 { 3055 SmNode::Prepare(rFormat, rDocShell); 3056 3057 //! hier muss/sollte es lediglich nicht der StarMath Font sein, 3058 //! damit fuer das in Arrange verwendete Zeichen ein "normales" 3059 //! (ungecliptes) Rechteck erzeugt wird. 3060 GetFont() = rFormat.GetFont(FNT_VARIABLE); 3061 3062 Flags() |= FLG_FONT | FLG_BOLD | FLG_ITALIC; 3063 } 3064 3065 3066 void SmBlankNode::Arrange(const OutputDevice &rDev, const SmFormat &rFormat) 3067 { 3068 SmTmpDevice aTmpDev ((OutputDevice &) rDev, sal_True); 3069 aTmpDev.SetFont(GetFont()); 3070 3071 // Abstand von der Fonthoehe abhaengig machen 3072 // (damit er beim skalieren (zB size *2 {a ~ b}) mitwaechst) 3073 long nDist = GetFont().GetSize().Height() / 10L, 3074 nSpace = nNum * nDist; 3075 3076 // ein SmRect mit Baseline und allem drum und dran besorgen 3077 SmRect::operator = (SmRect(aTmpDev, &rFormat, XubString(xub_Unicode(' ')), 3078 GetFont().GetBorderWidth())); 3079 3080 // und dieses auf die gewuenschte Breite bringen 3081 SetItalicSpaces(0, 0); 3082 SetWidth(nSpace); 3083 } 3084 3085 3086 3087