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