1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <osl/diagnose.h>
21 #include <vcl/svapp.hxx>
22 #include <vcl/wrkwin.hxx>
23 #include <vcl/virdev.hxx>
28 #include "utility.hxx"
32 ////////////////////////////////////////////////////////////////////////////////
35 // '\0' terminated Array with symbol, which should be treat as letters in
36 // StarMath Font, (to get a normal (non-clipped) SmRect in contrast to the
37 // other operators and symbols).
38 static sal_Unicode
const aMathAlpha
[] =
40 MS_ALEPH
, MS_IM
, MS_RE
,
41 MS_WP
, sal_Unicode(0xE070), MS_EMPTYSET
,
42 sal_Unicode(0x2113), sal_Unicode(0xE0D6), sal_Unicode(0x2107),
43 sal_Unicode(0x2127), sal_Unicode(0x210A), MS_HBAR
,
44 MS_LAMBDABAR
, MS_SETN
, MS_SETZ
,
45 MS_SETQ
, MS_SETR
, MS_SETC
,
46 sal_Unicode(0x2373), sal_Unicode(0xE0A5), sal_Unicode(0x2112),
47 sal_Unicode(0x2130), sal_Unicode(0x2131),
51 bool SmIsMathAlpha(const OUString
&rText
)
52 // true iff symbol (from StarMath Font) should be treated as letter
57 OSL_ENSURE(rText
.getLength() == 1, "Sm : string must be exactly one character long");
58 sal_Unicode cChar
= rText
[0];
60 // is it a greek symbol?
61 if (sal_Unicode(0xE0AC) <= cChar
&& cChar
<= sal_Unicode(0xE0D4))
65 // appears it in 'aMathAlpha'?
66 const sal_Unicode
*pChar
= aMathAlpha
;
67 while (*pChar
&& *pChar
!= cChar
)
69 return *pChar
!= sal_Unicode('\0');
74 ////////////////////////////////////////
81 // constructs empty rectangle at (0, 0) with width and height 0.
83 OSL_ENSURE(aTopLeft
== Point(0, 0), "Sm: ooops...");
84 OSL_ENSURE(aSize
== Size(0, 0), "Sm: ooops...");
86 bHasBaseline
= bHasAlignInfo
= false;
87 nBaseline
= nAlignT
= nAlignM
= nAlignB
=
88 nGlyphTop
= nGlyphBottom
=
89 nItalicLeftSpace
= nItalicRightSpace
=
90 nLoAttrFence
= nHiAttrFence
= 0;
95 SmRect::SmRect(const SmRect
&rRect
)
96 : aTopLeft(rRect
.aTopLeft
),
99 bHasBaseline
= rRect
.bHasBaseline
;
100 nBaseline
= rRect
.nBaseline
;
101 nAlignT
= rRect
.nAlignT
;
102 nAlignM
= rRect
.nAlignM
;
103 nAlignB
= rRect
.nAlignB
;
104 nGlyphTop
= rRect
.nGlyphTop
;
105 nGlyphBottom
= rRect
.nGlyphBottom
;
106 nHiAttrFence
= rRect
.nHiAttrFence
;
107 nLoAttrFence
= rRect
.nLoAttrFence
;
108 bHasAlignInfo
= rRect
.bHasAlignInfo
;
109 nItalicLeftSpace
= rRect
.nItalicLeftSpace
;
110 nItalicRightSpace
= rRect
.nItalicRightSpace
;
111 nBorderWidth
= rRect
.nBorderWidth
;
115 void SmRect::CopyAlignInfo(const SmRect
&rRect
)
117 nBaseline
= rRect
.nBaseline
;
118 bHasBaseline
= rRect
.bHasBaseline
;
119 nAlignT
= rRect
.nAlignT
;
120 nAlignM
= rRect
.nAlignM
;
121 nAlignB
= rRect
.nAlignB
;
122 bHasAlignInfo
= rRect
.bHasAlignInfo
;
123 nLoAttrFence
= rRect
.nLoAttrFence
;
124 nHiAttrFence
= rRect
.nHiAttrFence
;
128 void SmRect::BuildRect(const OutputDevice
&rDev
, const SmFormat
*pFormat
,
129 const OUString
&rText
, sal_uInt16 nBorder
)
131 OSL_ENSURE(aTopLeft
== Point(0, 0), "Sm: Ooops...");
133 aSize
= Size(rDev
.GetTextWidth(rText
), rDev
.GetTextHeight());
135 const FontMetric
aFM (rDev
.GetFontMetric());
136 bool bIsMath
= aFM
.GetName().EqualsIgnoreCaseAscii( FONTNAME_MATH
);
137 bool bAllowSmaller
= bIsMath
&& !SmIsMathAlpha(rText
);
138 const long nFontHeight
= rDev
.GetFont().GetSize().Height();
140 nBorderWidth
= nBorder
;
141 bHasAlignInfo
= true;
143 nBaseline
= aFM
.GetAscent();
144 nAlignT
= nBaseline
- nFontHeight
* 750L / 1000L;
145 nAlignM
= nBaseline
- nFontHeight
* 121L / 422L;
146 // that's where the horizontal bars of '+', '-', ... are
147 // (1/3 of ascent over baseline)
148 // (121 = 1/3 of 12pt ascent, 422 = 12pt fontheight)
151 // workaround for printer fonts with very small (possible 0 or even
152 // negative(!)) leading
153 if (aFM
.GetIntLeading() < 5 && rDev
.GetOutDevType() == OUTDEV_PRINTER
)
155 OutputDevice
*pWindow
= Application::GetDefaultDevice();
157 pWindow
->Push(PUSH_MAPMODE
| PUSH_FONT
);
159 pWindow
->SetMapMode(rDev
.GetMapMode());
160 pWindow
->SetFont(rDev
.GetFontMetric());
162 long nDelta
= pWindow
->GetFontMetric().GetIntLeading();
164 { // this value approx. fits a Leading of 80 at a
165 // Fontheight of 422 (12pt)
166 nDelta
= nFontHeight
* 8L / 43;
168 SetTop(GetTop() - nDelta
);
173 // get GlyphBoundRect
174 Rectangle aGlyphRect
;
175 #if OSL_DEBUG_LEVEL > 1
178 SmGetGlyphBoundRect(rDev
, rText
, aGlyphRect
);
179 #if OSL_DEBUG_LEVEL > 1
182 OSL_FAIL( "Sm : Ooops... (fehlt evtl. der Font?)");
186 nItalicLeftSpace
= GetLeft() - aGlyphRect
.Left() + nBorderWidth
;
187 nItalicRightSpace
= aGlyphRect
.Right() - GetRight() + nBorderWidth
;
188 if (nItalicLeftSpace
< 0 && !bAllowSmaller
)
189 nItalicLeftSpace
= 0;
190 if (nItalicRightSpace
< 0 && !bAllowSmaller
)
191 nItalicRightSpace
= 0;
195 nDist
= (rDev
.GetFont().GetSize().Height()
196 * pFormat
->GetDistance(DIS_ORNAMENTSIZE
)) / 100L;
198 nHiAttrFence
= aGlyphRect
.TopLeft().Y() - 1 - nBorderWidth
- nDist
;
199 nLoAttrFence
= SmFromTo(GetAlignB(), GetBottom(), 0.0);
201 nGlyphTop
= aGlyphRect
.Top() - nBorderWidth
;
202 nGlyphBottom
= aGlyphRect
.Bottom() + nBorderWidth
;
206 // for symbols and operators from the StarMath Font
207 // we adjust upper and lower margin of the symbol
209 SetBottom(nGlyphBottom
);
212 if (nHiAttrFence
< GetTop())
213 nHiAttrFence
= GetTop();
215 if (nLoAttrFence
> GetBottom())
216 nLoAttrFence
= GetBottom();
218 OSL_ENSURE(rText
.isEmpty() || !IsEmpty(),
219 "Sm: empty rectangle created");
223 void SmRect::Init(const OutputDevice
&rDev
, const SmFormat
*pFormat
,
224 const OUString
&rText
, sal_uInt16 nEBorderWidth
)
225 // get rectangle fitting for drawing 'rText' on OutputDevice 'rDev'
227 BuildRect(rDev
, pFormat
, rText
, nEBorderWidth
);
231 SmRect::SmRect(const OutputDevice
&rDev
, const SmFormat
*pFormat
,
232 const OUString
&rText
, long nEBorderWidth
)
234 OSL_ENSURE( nEBorderWidth
>= 0, "BorderWidth is negative" );
235 if (nEBorderWidth
< 0)
237 Init(rDev
, pFormat
, rText
, (sal_uInt16
) nEBorderWidth
);
241 SmRect::SmRect(long nWidth
, long nHeight
)
242 // this constructor should never be used for anything textlike because
243 // it will not provide useful values for baseline, AlignT and AlignB!
244 // It's purpose is to get a 'SmRect' for the horizontal line in fractions
245 // as used in 'SmBinVerNode'.
246 : aSize(nWidth
, nHeight
)
248 OSL_ENSURE(aTopLeft
== Point(0, 0), "Sm: ooops...");
250 bHasBaseline
= false;
251 bHasAlignInfo
= true;
254 nAlignB
= GetBottom();
255 nAlignM
= (nAlignT
+ nAlignB
) / 2; // this is the default
256 nItalicLeftSpace
= nItalicRightSpace
= 0;
257 nGlyphTop
= nHiAttrFence
= GetTop();
258 nGlyphBottom
= nLoAttrFence
= GetBottom();
263 void SmRect::SetLeft(long nLeft
)
265 if (nLeft
<= GetRight())
266 { aSize
.Width() = GetRight() - nLeft
+ 1;
267 aTopLeft
.X() = nLeft
;
272 void SmRect::SetRight(long nRight
)
274 if (nRight
>= GetLeft())
275 aSize
.Width() = nRight
- GetLeft() + 1;
279 void SmRect::SetBottom(long nBottom
)
281 if (nBottom
>= GetTop())
282 aSize
.Height() = nBottom
- GetTop() + 1;
286 void SmRect::SetTop(long nTop
)
288 if (nTop
<= GetBottom())
289 { aSize
.Height() = GetBottom() - nTop
+ 1;
295 void SmRect::Move(const Point
&rPosition
)
296 // move rectangle by position 'rPosition'.
298 aTopLeft
+= rPosition
;
300 long nDelta
= rPosition
.Y();
306 nGlyphBottom
+= nDelta
;
307 nHiAttrFence
+= nDelta
;
308 nLoAttrFence
+= nDelta
;
312 const Point
SmRect::AlignTo(const SmRect
&rRect
, RectPos ePos
,
313 RectHorAlign eHor
, RectVerAlign eVer
) const
314 { Point
aPos (GetTopLeft());
315 // will become the topleft point of the new rectangle position
317 // set horizontal or vertical new rectangle position depending on
318 // 'ePos' is one of 'RP_LEFT', 'RP_RIGHT' or 'RP_TOP', 'RP_BOTTOM'
321 aPos
.X() = rRect
.GetItalicLeft() - GetItalicRightSpace()
325 aPos
.X() = rRect
.GetItalicRight() + 1 + GetItalicLeftSpace();
328 aPos
.Y() = rRect
.GetTop() - GetHeight();
331 aPos
.Y() = rRect
.GetBottom() + 1;
334 aPos
.X() = rRect
.GetItalicCenterX() - GetItalicWidth() / 2
335 + GetItalicLeftSpace();
338 OSL_FAIL("Sm: unknown case");
341 // check if horizontal position is already set
342 if (ePos
== RP_LEFT
|| ePos
== RP_RIGHT
|| ePos
== RP_ATTRIBUT
)
343 // correct error in current vertical position
346 aPos
.Y() += rRect
.GetAlignT() - GetAlignT();
349 aPos
.Y() += rRect
.GetAlignM() - GetAlignM();
352 // align baselines if possible else align mid's
353 if (HasBaseline() && rRect
.HasBaseline())
354 aPos
.Y() += rRect
.GetBaseline() - GetBaseline();
356 aPos
.Y() += rRect
.GetAlignM() - GetAlignM();
359 aPos
.Y() += rRect
.GetAlignB() - GetAlignB();
362 aPos
.Y() += rRect
.GetCenterY() - GetCenterY();
364 case RVA_ATTRIBUT_HI
:
365 aPos
.Y() += rRect
.GetHiAttrFence() - GetBottom();
367 case RVA_ATTRIBUT_MID
:
368 aPos
.Y() += SmFromTo(rRect
.GetAlignB(), rRect
.GetAlignT(), 0.4)
371 case RVA_ATTRIBUT_LO
:
372 aPos
.Y() += rRect
.GetLoAttrFence() - GetTop();
375 OSL_FAIL("Sm: unknown case");
378 // check if vertical position is already set
379 if (ePos
== RP_TOP
|| ePos
== RP_BOTTOM
)
380 // correct error in current horizontal position
383 aPos
.X() += rRect
.GetItalicLeft() - GetItalicLeft();
386 aPos
.X() += rRect
.GetItalicCenterX() - GetItalicCenterX();
389 aPos
.X() += rRect
.GetItalicRight() - GetItalicRight();
392 OSL_FAIL("Sm: unknown case");
399 SmRect
& SmRect::Union(const SmRect
&rRect
)
400 // rectangle union of current one with 'rRect'. The result is to be the
401 // smallest rectangles that covers the space of both rectangles.
402 // (empty rectangles cover no space)
403 //! Italic correction is NOT taken into account here!
408 long nL
= rRect
.GetLeft(),
409 nR
= rRect
.GetRight(),
411 nB
= rRect
.GetBottom(),
412 nGT
= rRect
.nGlyphTop
,
413 nGB
= rRect
.nGlyphBottom
;
417 if ((nTmp
= GetLeft()) < nL
)
419 if ((nTmp
= GetRight()) > nR
)
421 if ((nTmp
= GetTop()) < nT
)
423 if ((nTmp
= GetBottom()) > nB
)
425 if ((nTmp
= nGlyphTop
) < nGT
)
427 if ((nTmp
= nGlyphBottom
) > nGB
)
442 SmRect
& SmRect::ExtendBy(const SmRect
&rRect
, RectCopyMBL eCopyMode
)
443 // let current rectangle be the union of itself and 'rRect'
444 // (the smallest rectangle surrounding both). Also adapt values for
445 // 'AlignT', 'AlignM', 'AlignB', baseline and italic-spaces.
446 // The baseline is set according to 'eCopyMode'.
447 // If one of the rectangles has no relevant info the other one is copied.
449 // get some values used for (italic) spaces adaption
450 // ! (need to be done before changing current SmRect) !
451 long nL
= std::min(GetItalicLeft(), rRect
.GetItalicLeft()),
452 nR
= std::max(GetItalicRight(), rRect
.GetItalicRight());
456 SetItalicSpaces(GetLeft() - nL
, nR
- GetRight());
459 CopyAlignInfo(rRect
);
460 else if (rRect
.HasAlignInfo())
461 { nAlignT
= std::min(GetAlignT(), rRect
.GetAlignT());
462 nAlignB
= std::max(GetAlignB(), rRect
.GetAlignB());
463 nHiAttrFence
= std::min(GetHiAttrFence(), rRect
.GetHiAttrFence());
464 nLoAttrFence
= std::max(GetLoAttrFence(), rRect
.GetLoAttrFence());
465 OSL_ENSURE(HasAlignInfo(), "Sm: ooops...");
476 nAlignM
= (nAlignT
+ nAlignB
) / 2;
483 OSL_FAIL("Sm: unknown case");
491 SmRect
& SmRect::ExtendBy(const SmRect
&rRect
, RectCopyMBL eCopyMode
,
493 // as 'ExtendBy' but sets AlignM value to 'nNewAlignM'.
494 // (this version will be used in 'SmBinVerNode' to provide means to
495 // align eg "{a over b} over c" correctly where AlignM should not
496 // be (AlignT + AlignB) / 2)
498 OSL_ENSURE(HasAlignInfo(), "Sm: no align info");
500 ExtendBy(rRect
, eCopyMode
);
501 nAlignM
= nNewAlignM
;
507 SmRect
& SmRect::ExtendBy(const SmRect
&rRect
, RectCopyMBL eCopyMode
,
508 bool bKeepVerAlignParams
)
509 // as 'ExtendBy' but keeps original values for AlignT, -M and -B and
511 // (this is used in 'SmSupSubNode' where the sub-/supscripts shouldn't
512 // be allowed to modify these values.)
514 long nOldAlignT
= GetAlignT(),
515 nOldAlignM
= GetAlignM(),
516 nOldAlignB
= GetAlignB(),
517 nOldBaseline
= nBaseline
; //! depends not on 'HasBaseline'
518 bool bOldHasAlignInfo
= HasAlignInfo();
520 ExtendBy(rRect
, eCopyMode
);
522 if (bKeepVerAlignParams
)
523 { nAlignT
= nOldAlignT
;
524 nAlignM
= nOldAlignM
;
525 nAlignB
= nOldAlignB
;
526 nBaseline
= nOldBaseline
;
527 bHasAlignInfo
= bOldHasAlignInfo
;
534 long SmRect::OrientedDist(const Point
&rPoint
) const
535 // return oriented distance of rPoint to the current rectangle,
536 // especially the return value is <= 0 iff the point is inside the
538 // For simplicity the maximum-norm is used.
540 bool bIsInside
= IsInsideItalicRect(rPoint
);
542 // build reference point to define the distance
545 { Point
aIC (GetItalicCenterX(), GetCenterY());
547 aRef
.X() = rPoint
.X() >= aIC
.X() ? GetItalicRight() : GetItalicLeft();
548 aRef
.Y() = rPoint
.Y() >= aIC
.Y() ? GetBottom() : GetTop();
553 if (rPoint
.X() > GetItalicRight())
554 aRef
.X() = GetItalicRight();
555 else if (rPoint
.X() < GetItalicLeft())
556 aRef
.X() = GetItalicLeft();
558 aRef
.X() = rPoint
.X();
560 if (rPoint
.Y() > GetBottom())
561 aRef
.Y() = GetBottom();
562 else if (rPoint
.Y() < GetTop())
565 aRef
.Y() = rPoint
.Y();
568 // build distance vector
569 Point
aDist (aRef
- rPoint
);
571 long nAbsX
= labs(aDist
.X()),
572 nAbsY
= labs(aDist
.Y());
574 return bIsInside
? - std::min(nAbsX
, nAbsY
) : std::max (nAbsX
, nAbsY
);
578 bool SmRect::IsInsideRect(const Point
&rPoint
) const
580 return rPoint
.Y() >= GetTop()
581 && rPoint
.Y() <= GetBottom()
582 && rPoint
.X() >= GetLeft()
583 && rPoint
.X() <= GetRight();
587 bool SmRect::IsInsideItalicRect(const Point
&rPoint
) const
589 return rPoint
.Y() >= GetTop()
590 && rPoint
.Y() <= GetBottom()
591 && rPoint
.X() >= GetItalicLeft()
592 && rPoint
.X() <= GetItalicRight();
595 SmRect
SmRect::AsGlyphRect() const
597 SmRect
aRect (*this);
598 aRect
.SetTop(nGlyphTop
);
599 aRect
.SetBottom(nGlyphBottom
);
603 bool SmGetGlyphBoundRect(const OutputDevice
&rDev
,
604 const OUString
&rText
, Rectangle
&rRect
)
605 // basically the same as 'GetTextBoundRect' (in class 'OutputDevice')
606 // but with a string as argument.
608 // handle special case first
615 // get a device where 'OutputDevice::GetTextBoundRect' will be successful
616 OutputDevice
*pGlyphDev
;
617 if (rDev
.GetOutDevType() != OUTDEV_PRINTER
)
618 pGlyphDev
= (OutputDevice
*) &rDev
;
621 // since we format for the printer (where GetTextBoundRect will fail)
622 // we need a virtual device here.
623 pGlyphDev
= &SM_MOD()->GetDefaultVirtualDev();
626 const FontMetric
aDevFM (rDev
.GetFontMetric());
628 pGlyphDev
->Push(PUSH_FONT
| PUSH_MAPMODE
);
629 Font
aFnt(rDev
.GetFont());
630 aFnt
.SetAlign(ALIGN_TOP
);
632 // use scale factor when calling GetTextBoundRect to counter
633 // negative effects from antialiasing which may otherwise result
634 // in significant incorrect bounding rectangles for some characters.
635 Size aFntSize
= aFnt
.GetSize();
637 // Workaround to avoid HUGE font sizes and resulting problems
638 long nScaleFactor
= 1;
639 while( aFntSize
.Height() > 2000 * nScaleFactor
)
642 aFnt
.SetSize( Size( aFntSize
.Width() / nScaleFactor
, aFntSize
.Height() / nScaleFactor
) );
643 pGlyphDev
->SetFont(aFnt
);
645 long nTextWidth
= rDev
.GetTextWidth(rText
);
647 Rectangle
aResult (aPoint
, Size(nTextWidth
, rDev
.GetTextHeight())),
650 bool bSuccess
= pGlyphDev
->GetTextBoundRect(aTmp
, rText
, 0, 0);
651 OSL_ENSURE( bSuccess
, "GetTextBoundRect failed" );
656 aResult
= Rectangle(aTmp
.Left() * nScaleFactor
, aTmp
.Top() * nScaleFactor
,
657 aTmp
.Right() * nScaleFactor
, aTmp
.Bottom() * nScaleFactor
);
658 if (&rDev
!= pGlyphDev
) /* only when rDev is a printer... */
660 long nGDTextWidth
= pGlyphDev
->GetTextWidth(rText
);
661 if (nGDTextWidth
!= 0 &&
662 nTextWidth
!= nGDTextWidth
)
664 aResult
.Right() *= nTextWidth
;
665 aResult
.Right() /= nGDTextWidth
* nScaleFactor
;
670 // move rectangle to match possibly different baselines
671 // (because of different devices)
672 long nDelta
= aDevFM
.GetAscent() - pGlyphDev
->GetFontMetric().GetAscent() * nScaleFactor
;
673 aResult
.Move(0, nDelta
);
682 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */