xref: /trunk/main/starmath/source/rect.cxx (revision d107581f)
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 
28 #include <tools/string.hxx>
29 #include <tools/debug.hxx>
30 #include <vcl/svapp.hxx>
31 #include <vcl/wrkwin.hxx>
32 #include <vcl/virdev.hxx>
33 
34 
35 #include "rect.hxx"
36 #include "types.hxx"
37 #include "utility.hxx"
38 #include "smmod.hxx"
39 
40 
41 ////////////////////////////////////////////////////////////////////////////////
42 
43 
44 // '\0' terminiertes Array mit Zeichen, die im StarMath Font als Buchstaben
45 // betrachtet werden sollen, (um im Gegensatz zu den anderen Operatoren
46 // und Symbolen ein "normales"(ungecliptes) SmRect zu erhalten).
47 static xub_Unicode __READONLY_DATA aMathAlpha[] =
48 {
49     MS_ALEPH,               MS_IM,                  MS_RE,
50     MS_WP,                  xub_Unicode(0xE070),    MS_EMPTYSET,
51     xub_Unicode(0x2113),    xub_Unicode(0xE0D6),    xub_Unicode(0x2107),
52     xub_Unicode(0x2127),    xub_Unicode(0x210A),    MS_HBAR,
53     MS_LAMBDABAR,           MS_SETN,                MS_SETZ,
54     MS_SETQ,                MS_SETR,                MS_SETC,
55     xub_Unicode(0x2373),    xub_Unicode(0xE0A5),    xub_Unicode(0x2112),
56     xub_Unicode(0x2130),    xub_Unicode(0x2131),
57 	xub_Unicode('\0')
58 };
59 
SmIsMathAlpha(const XubString & rText)60 sal_Bool SmIsMathAlpha(const XubString &rText)
61 	// ergibt genau dann sal_True, wenn das Zeichen (aus dem StarMath Font) wie ein
62 	// Buchstabe behandelt werden soll.
63 {
64 	if (rText.Len() == 0)
65 		return sal_False;
66 
67     DBG_ASSERT(rText.Len() == 1, "Sm : String enthaelt nicht genau ein Zeichen");
68 	xub_Unicode cChar = rText.GetChar(0);
69 
70 	// ist es ein griechisches Zeichen ?
71     if (xub_Unicode(0xE0AC) <= cChar  &&  cChar <= xub_Unicode(0xE0D4))
72 		return sal_True;
73 	else
74 	{
75 		// kommt es in 'aMathAlpha' vor ?
76 		const xub_Unicode *pChar = aMathAlpha;
77 		while (*pChar  &&  *pChar != cChar)
78 			pChar++;
79 		return *pChar != xub_Unicode('\0');
80 	}
81 }
82 
83 
84 ////////////////////////////////////////
85 //
86 // SmRect members
87 //
88 
89 
SmRect()90 SmRect::SmRect()
91 	// constructs empty rectangle at (0, 0) with width and height 0.
92 {
93 	DBG_ASSERT(aTopLeft == Point(0, 0), "Sm: ooops...");
94 	DBG_ASSERT(aSize == Size(0, 0), "Sm: ooops...");
95 
96 	bHasBaseline = bHasAlignInfo = sal_False;
97 	nBaseline = nAlignT = nAlignM = nAlignB =
98 	nGlyphTop = nGlyphBottom =
99 	nItalicLeftSpace = nItalicRightSpace =
100 	nLoAttrFence = nHiAttrFence = 0;
101     nBorderWidth = 0;
102 }
103 
104 
SmRect(const SmRect & rRect)105 SmRect::SmRect(const SmRect &rRect)
106 :	aTopLeft(rRect.aTopLeft),
107 	aSize(rRect.aSize)
108 {
109 	bHasBaseline  = rRect.bHasBaseline;
110 	nBaseline	  = rRect.nBaseline;
111 	nAlignT		  = rRect.nAlignT;
112 	nAlignM		  = rRect.nAlignM;
113 	nAlignB		  = rRect.nAlignB;
114 	nGlyphTop	  = rRect.nGlyphTop;
115 	nGlyphBottom  = rRect.nGlyphBottom;
116 	nHiAttrFence  = rRect.nHiAttrFence;
117 	nLoAttrFence  = rRect.nLoAttrFence;
118 	bHasAlignInfo = rRect.bHasAlignInfo;
119 	nItalicLeftSpace  = rRect.nItalicLeftSpace;
120 	nItalicRightSpace = rRect.nItalicRightSpace;
121     nBorderWidth  = rRect.nBorderWidth;
122 }
123 
124 
CopyAlignInfo(const SmRect & rRect)125 void SmRect::CopyAlignInfo(const SmRect &rRect)
126 {
127 	nBaseline	  = rRect.nBaseline;
128 	bHasBaseline  =	rRect.bHasBaseline;
129 	nAlignT		  =	rRect.nAlignT;
130 	nAlignM		  =	rRect.nAlignM;
131 	nAlignB		  =	rRect.nAlignB;
132 	bHasAlignInfo = rRect.bHasAlignInfo;
133 	nLoAttrFence  =	rRect.nLoAttrFence;
134 	nHiAttrFence  =	rRect.nHiAttrFence;
135 }
136 
137 
BuildRect(const OutputDevice & rDev,const SmFormat * pFormat,const XubString & rText,sal_uInt16 nBorder)138 void SmRect::BuildRect(const OutputDevice &rDev, const SmFormat *pFormat,
139                        const XubString &rText, sal_uInt16 nBorder)
140 {
141 	DBG_ASSERT(aTopLeft == Point(0, 0), "Sm: Ooops...");
142 
143 	aSize = Size(rDev.GetTextWidth(rText), rDev.GetTextHeight());
144 
145 	const FontMetric  aFM (rDev.GetFontMetric());
146     sal_Bool              bIsMath  = aFM.GetName().EqualsIgnoreCaseAscii( FONTNAME_MATH );
147 	sal_Bool			  bAllowSmaller = bIsMath && !SmIsMathAlpha(rText);
148 	const long		  nFontHeight = rDev.GetFont().GetSize().Height();
149 
150     nBorderWidth  = nBorder;
151 	bHasAlignInfo = sal_True;
152 	bHasBaseline  = sal_True;
153 	nBaseline	  = aFM.GetAscent();
154 	nAlignT		  = nBaseline - nFontHeight * 750L / 1000L;
155 	nAlignM 	  = nBaseline - nFontHeight * 121L / 422L;
156 		// that's where the horizontal bars of '+', '-', ... are
157 		// (1/3 of ascent over baseline)
158 		// (121 = 1/3 of 12pt ascent, 422 = 12pt fontheight)
159 	nAlignB		  = nBaseline;
160 
161 	// workaround for printer fonts with very small (possible 0 or even
162 	// negative(!)) leading
163 	if (aFM.GetIntLeading() < 5  &&  rDev.GetOutDevType() == OUTDEV_PRINTER)
164 	{
165 		OutputDevice	*pWindow = Application::GetDefaultDevice();
166 
167 		pWindow->Push(PUSH_MAPMODE | PUSH_FONT);
168 
169 		pWindow->SetMapMode(rDev.GetMapMode());
170 		pWindow->SetFont(rDev.GetFontMetric());
171 
172 		long  nDelta = pWindow->GetFontMetric().GetIntLeading();
173 		if (nDelta == 0)
174 		{ 	// dieser Wert entspricht etwa einem Leading von 80 bei einer
175             // Fonthoehe von 422 (12pt)
176 			nDelta = nFontHeight * 8L / 43;
177 		}
178 		SetTop(GetTop() - nDelta);
179 
180 		pWindow->Pop();
181 	}
182 
183 	// get GlyphBoundRect
184 	Rectangle  aGlyphRect;
185 #if OSL_DEBUG_LEVEL > 1
186     sal_Bool bSuccess =
187 #endif
188                 SmGetGlyphBoundRect(rDev, rText, aGlyphRect);
189 #if OSL_DEBUG_LEVEL > 1
190     if (!bSuccess)
191     {
192         DBG_ERROR( "Sm : Ooops... (fehlt evtl. der Font?)");
193     }
194 #endif
195 
196 	nItalicLeftSpace  = GetLeft() - aGlyphRect.Left() + nBorderWidth;
197 	nItalicRightSpace = aGlyphRect.Right() - GetRight() + nBorderWidth;
198 	if (nItalicLeftSpace  < 0  &&  !bAllowSmaller)
199 		nItalicLeftSpace  = 0;
200 	if (nItalicRightSpace < 0  &&  !bAllowSmaller)
201 		nItalicRightSpace = 0;
202 
203 	long  nDist = 0;
204 	if (pFormat)
205 		nDist = (rDev.GetFont().GetSize().Height()
206 				* pFormat->GetDistance(DIS_ORNAMENTSIZE)) / 100L;
207 
208 	nHiAttrFence = aGlyphRect.TopLeft().Y() - 1 - nBorderWidth - nDist;
209 	nLoAttrFence = SmFromTo(GetAlignB(), GetBottom(), 0.0);
210 
211 	nGlyphTop    = aGlyphRect.Top() - nBorderWidth;
212 	nGlyphBottom = aGlyphRect.Bottom() + nBorderWidth;
213 
214 	if (bAllowSmaller)
215 	{
216         // fuer Symbole und Operatoren aus dem StarMath Font passen wir den
217 		// oberen und unteren Rand dem Zeichen an.
218 		SetTop(nGlyphTop);
219 		SetBottom(nGlyphBottom);
220 	}
221 
222 	if (nHiAttrFence < GetTop())
223 		nHiAttrFence = GetTop();
224 
225 	if (nLoAttrFence > GetBottom())
226 		nLoAttrFence = GetBottom();
227 
228 	DBG_ASSERT(rText.Len() == 0  ||  !IsEmpty(),
229 			   "Sm: leeres Rechteck erzeugt");
230 }
231 
232 
Init(const OutputDevice & rDev,const SmFormat * pFormat,const XubString & rText,sal_uInt16 nEBorderWidth)233 void SmRect::Init(const OutputDevice &rDev, const SmFormat *pFormat,
234                   const XubString &rText, sal_uInt16 nEBorderWidth)
235 	// get rectangle fitting for drawing 'rText' on OutputDevice 'rDev'
236 {
237     BuildRect(rDev, pFormat, rText, nEBorderWidth);
238 }
239 
240 
SmRect(const OutputDevice & rDev,const SmFormat * pFormat,const XubString & rText,long nEBorderWidth)241 SmRect::SmRect(const OutputDevice &rDev, const SmFormat *pFormat,
242                const XubString &rText, long nEBorderWidth)
243 {
244     DBG_ASSERT( nEBorderWidth >= 0, "BorderWidth negativ" );
245     if (nEBorderWidth < 0)
246         nEBorderWidth = 0;
247     Init(rDev, pFormat, rText, (sal_uInt16) nEBorderWidth);
248 }
249 
250 
SmRect(long nWidth,long nHeight)251 SmRect::SmRect(long nWidth, long nHeight)
252 	// this constructor should never be used for anything textlike because
253 	// it will not provide useful values for baseline, AlignT and AlignB!
254 	// It's purpose is to get a 'SmRect' for the horizontal line in fractions
255 	// as used in 'SmBinVerNode'.
256 :	aSize(nWidth, nHeight)
257 {
258 	DBG_ASSERT(aTopLeft == Point(0, 0), "Sm: ooops...");
259 
260 	bHasBaseline  = sal_False;
261 	bHasAlignInfo = sal_True;
262 	nBaseline	  = 0;
263 	nAlignT		  = GetTop();
264 	nAlignB		  = GetBottom();
265 	nAlignM		  = (nAlignT + nAlignB) / 2;		// this is the default
266 	nItalicLeftSpace = nItalicRightSpace = 0;
267 	nGlyphTop    = nHiAttrFence  = GetTop();
268 	nGlyphBottom = nLoAttrFence  = GetBottom();
269     nBorderWidth  = 0;
270 }
271 
272 
SetLeft(long nLeft)273 void SmRect::SetLeft(long nLeft)
274 {
275 	if (nLeft <= GetRight())
276 	{	aSize.Width() = GetRight() - nLeft + 1;
277 		aTopLeft.X()  = nLeft;
278 	}
279 }
280 
281 
SetRight(long nRight)282 void SmRect::SetRight(long nRight)
283 {
284 	if (nRight >= GetLeft())
285 		aSize.Width() = nRight - GetLeft() + 1;
286 }
287 
288 
SetBottom(long nBottom)289 void SmRect::SetBottom(long nBottom)
290 {
291 	if (nBottom >= GetTop())
292 		aSize.Height() = nBottom - GetTop() + 1;
293 }
294 
295 
SetTop(long nTop)296 void SmRect::SetTop(long nTop)
297 {
298 	if (nTop <= GetBottom())
299 	{	aSize.Height()	 = GetBottom() - nTop + 1;
300 		aTopLeft.Y() = nTop;
301 	}
302 }
303 
304 
Move(const Point & rPosition)305 void SmRect::Move(const Point &rPosition)
306 	// move rectangle by position 'rPosition'.
307 {
308 	aTopLeft  += rPosition;
309 
310 	long  nDelta = rPosition.Y();
311 	nBaseline += nDelta;
312 	nAlignT   += nDelta;
313 	nAlignM	  += nDelta;
314 	nAlignB   += nDelta;
315 	nGlyphTop    += nDelta;
316 	nGlyphBottom += nDelta;
317 	nHiAttrFence += nDelta;
318 	nLoAttrFence += nDelta;
319 }
320 
321 
AlignTo(const SmRect & rRect,RectPos ePos,RectHorAlign eHor,RectVerAlign eVer) const322 const Point SmRect::AlignTo(const SmRect &rRect, RectPos ePos,
323 							RectHorAlign eHor, RectVerAlign eVer) const
324 {	Point  aPos (GetTopLeft());
325 		// will become the topleft point of the new rectangle position
326 
327 	// set horizontal or vertical new rectangle position depending on
328 	// 'ePos' is one of 'RP_LEFT', 'RP_RIGHT' or 'RP_TOP', 'RP_BOTTOM'
329 	switch (ePos)
330 	{	case RP_LEFT :
331 			aPos.X() = rRect.GetItalicLeft() - GetItalicRightSpace()
332 					   - GetWidth();
333 			break;
334 		case RP_RIGHT :
335 			aPos.X() = rRect.GetItalicRight() + 1 + GetItalicLeftSpace();
336 			break;
337 		case RP_TOP :
338 			aPos.Y() = rRect.GetTop() - GetHeight();
339 			break;
340 		case RP_BOTTOM :
341 			aPos.Y() = rRect.GetBottom() + 1;
342 			break;
343 		case RP_ATTRIBUT :
344 			aPos.X() = rRect.GetItalicCenterX() - GetItalicWidth() / 2
345 					   + GetItalicLeftSpace();
346 			break;
347 		default :
348 			DBG_ASSERT(sal_False, "Sm: unbekannter Fall");
349 	}
350 
351 	// check if horizontal position is already set
352 	if (ePos == RP_LEFT  ||  ePos == RP_RIGHT  ||  ePos == RP_ATTRIBUT)
353 		// correct error in current vertical position
354 		switch (eVer)
355 		{	case RVA_TOP :
356 				aPos.Y() += rRect.GetAlignT() - GetAlignT();
357 				break;
358 			case RVA_MID :
359 				aPos.Y() += rRect.GetAlignM() - GetAlignM();
360 				break;
361 			case RVA_BASELINE :
362 				// align baselines if possible else align mid's
363 				if (HasBaseline() && rRect.HasBaseline())
364 					aPos.Y() += rRect.GetBaseline() - GetBaseline();
365 				else
366 					aPos.Y() += rRect.GetAlignM() - GetAlignM();
367 				break;
368 			case RVA_BOTTOM :
369 				aPos.Y() += rRect.GetAlignB() - GetAlignB();
370 				break;
371 			case RVA_CENTERY :
372 				aPos.Y() += rRect.GetCenterY() - GetCenterY();
373 				break;
374 			case RVA_ATTRIBUT_HI:
375 				aPos.Y() += rRect.GetHiAttrFence() - GetBottom();
376 				break;
377 			case RVA_ATTRIBUT_MID :
378 				aPos.Y() += SmFromTo(rRect.GetAlignB(), rRect.GetAlignT(), 0.4)
379 							- GetCenterY();
380 				break;
381 			case RVA_ATTRIBUT_LO :
382 				aPos.Y() += rRect.GetLoAttrFence() - GetTop();
383 				break;
384 		default :
385 				DBG_ASSERT(sal_False, "Sm: unbekannter Fall");
386 		}
387 
388 	// check if vertical position is already set
389 	if (ePos == RP_TOP	||	ePos == RP_BOTTOM)
390 		// correct error in current horizontal position
391 		switch (eHor)
392 		{	case RHA_LEFT :
393 				aPos.X() += rRect.GetItalicLeft() - GetItalicLeft();
394 				break;
395 			case RHA_CENTER :
396 				aPos.X() += rRect.GetItalicCenterX() - GetItalicCenterX();
397 				break;
398 			case RHA_RIGHT :
399 				aPos.X() += rRect.GetItalicRight() - GetItalicRight();
400 				break;
401 			default :
402 				DBG_ASSERT(sal_False, "Sm: unbekannter Fall");
403 		}
404 
405 	return aPos;
406 }
407 
408 
Union(const SmRect & rRect)409 SmRect & SmRect::Union(const SmRect &rRect)
410 	// rectangle union of current one with 'rRect'. The result is to be the
411 	// smallest rectangles that covers the space of both rectangles.
412 	// (empty rectangles cover no space)
413 	//! Italic correction is NOT taken into account here!
414 {
415 	if (rRect.IsEmpty())
416 		return *this;
417 
418 	long  nL  = rRect.GetLeft(),
419 		  nR  = rRect.GetRight(),
420 		  nT  = rRect.GetTop(),
421 		  nB  = rRect.GetBottom(),
422 		  nGT = rRect.nGlyphTop,
423 		  nGB = rRect.nGlyphBottom;
424 	if (!IsEmpty())
425 	{	long  nTmp;
426 
427 		if ((nTmp = GetLeft()) < nL)
428 			nL = nTmp;
429 		if ((nTmp = GetRight()) > nR)
430 			nR = nTmp;
431 		if ((nTmp = GetTop()) < nT)
432 			nT = nTmp;
433 		if ((nTmp = GetBottom()) > nB)
434 			nB = nTmp;
435 		if ((nTmp = nGlyphTop) < nGT)
436 			nGT = nTmp;
437 		if ((nTmp = nGlyphBottom) > nGB)
438 			nGB = nTmp;
439 	}
440 
441 	SetLeft(nL);
442 	SetRight(nR);
443 	SetTop(nT);
444 	SetBottom(nB);
445 	nGlyphTop    = nGT;
446 	nGlyphBottom = nGB;
447 
448 	return *this;
449 }
450 
451 
ExtendBy(const SmRect & rRect,RectCopyMBL eCopyMode)452 SmRect & SmRect::ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode)
453 	// let current rectangle be the union of itself and 'rRect'
454 	// (the smallest rectangle surrounding both). Also adapt values for
455 	// 'AlignT', 'AlignM', 'AlignB', baseline and italic-spaces.
456 	// The baseline is set according to 'eCopyMode'.
457 	// If one of the rectangles has no relevant info the other one is copied.
458 {
459 	// get some values used for (italic) spaces adaption
460 	// ! (need to be done before changing current SmRect) !
461 	long  nL = Min(GetItalicLeft(),  rRect.GetItalicLeft()),
462 		  nR = Max(GetItalicRight(), rRect.GetItalicRight());
463 
464 	Union(rRect);
465 
466 	SetItalicSpaces(GetLeft() - nL, nR - GetRight());
467 
468 	if (!HasAlignInfo())
469 		CopyAlignInfo(rRect);
470 	else if (rRect.HasAlignInfo())
471 	{	nAlignT = Min(GetAlignT(), rRect.GetAlignT());
472 		nAlignB = Max(GetAlignB(), rRect.GetAlignB());
473 		nHiAttrFence = Min(GetHiAttrFence(), rRect.GetHiAttrFence());
474 		nLoAttrFence = Max(GetLoAttrFence(), rRect.GetLoAttrFence());
475 		DBG_ASSERT(HasAlignInfo(), "Sm: ooops...");
476 
477 		switch (eCopyMode)
478 		{	case RCP_THIS:
479 				// already done
480 				break;
481 			case RCP_ARG:
482 				CopyMBL(rRect);
483 				break;
484 			case RCP_NONE:
485 				ClearBaseline();
486 				nAlignM = (nAlignT + nAlignB) / 2;
487 				break;
488 			case RCP_XOR:
489 				if (!HasBaseline())
490 					CopyMBL(rRect);
491 				break;
492 			default :
493 				DBG_ASSERT(sal_False, "Sm: unbekannter Fall");
494 		}
495 	}
496 
497 	return *this;
498 }
499 
500 
ExtendBy(const SmRect & rRect,RectCopyMBL eCopyMode,long nNewAlignM)501 SmRect & SmRect::ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode,
502 						  long nNewAlignM)
503 	// as 'ExtendBy' but sets AlignM value to 'nNewAlignM'.
504 	// (this version will be used in 'SmBinVerNode' to provide means to
505 	// align eg "{a over b} over c" correctly where AlignM should not
506 	// be (AlignT + AlignB) / 2)
507 {
508 	DBG_ASSERT(HasAlignInfo(), "Sm: keine Align Info");
509 
510 	ExtendBy(rRect, eCopyMode);
511 	nAlignM = nNewAlignM;
512 
513 	return *this;
514 }
515 
516 
ExtendBy(const SmRect & rRect,RectCopyMBL eCopyMode,sal_Bool bKeepVerAlignParams)517 SmRect & SmRect::ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode,
518 						  sal_Bool bKeepVerAlignParams)
519 	// as 'ExtendBy' but keeps original values for AlignT, -M and -B and
520 	// baseline.
521 	// (this is used in 'SmSupSubNode' where the sub-/supscripts shouldn't
522 	// be allowed to modify these values.)
523 {
524 	long  nOldAlignT   = GetAlignT(),
525 		  nOldAlignM   = GetAlignM(),
526 		  nOldAlignB   = GetAlignB(),
527 		  nOldBaseline = nBaseline;		//! depends not on 'HasBaseline'
528 	sal_Bool  bOldHasAlignInfo = HasAlignInfo();
529 
530 	ExtendBy(rRect, eCopyMode);
531 
532 	if (bKeepVerAlignParams)
533 	{	nAlignT	  = nOldAlignT;
534 		nAlignM	  = nOldAlignM;
535 		nAlignB	  = nOldAlignB;
536 		nBaseline = nOldBaseline;
537 		bHasAlignInfo = bOldHasAlignInfo;
538 	}
539 
540 	return *this;
541 }
542 
543 
OrientedDist(const Point & rPoint) const544 long SmRect::OrientedDist(const Point &rPoint) const
545 	// return oriented distance of rPoint to the current rectangle,
546 	// especially the return value is <= 0 iff the point is inside the
547 	// rectangle.
548 	// For simplicity the maximum-norm is used.
549 {
550 	sal_Bool  bIsInside = IsInsideItalicRect(rPoint);
551 
552 	// build reference point to define the distance
553 	Point  aRef;
554 	if (bIsInside)
555 	{	Point  aIC (GetItalicCenterX(), GetCenterY());
556 
557 		aRef.X() = rPoint.X() >= aIC.X() ? GetItalicRight() : GetItalicLeft();
558 		aRef.Y() = rPoint.Y() >= aIC.Y() ? GetBottom() : GetTop();
559 	}
560 	else
561 	{
562 		// x-coordinate
563 		if (rPoint.X() > GetItalicRight())
564 			aRef.X() = GetItalicRight();
565 		else if (rPoint.X() < GetItalicLeft())
566 			aRef.X() = GetItalicLeft();
567 		else
568 			aRef.X() = rPoint.X();
569 		// y-coordinate
570 		if (rPoint.Y() > GetBottom())
571 			aRef.Y() = GetBottom();
572 		else if (rPoint.Y() < GetTop())
573 			aRef.Y() = GetTop();
574 		else
575 			aRef.Y() = rPoint.Y();
576 	}
577 
578 	// build distance vector
579 	Point  aDist (aRef - rPoint);
580 
581 	long nAbsX = labs(aDist.X()),
582 		 nAbsY = labs(aDist.Y());
583 
584 	return bIsInside ? - Min(nAbsX, nAbsY) : Max (nAbsX, nAbsY);
585 }
586 
587 
IsInsideRect(const Point & rPoint) const588 sal_Bool SmRect::IsInsideRect(const Point &rPoint) const
589 {
590 	return	   rPoint.Y() >= GetTop()
591 		   &&  rPoint.Y() <= GetBottom()
592 		   &&  rPoint.X() >= GetLeft()
593 		   &&  rPoint.X() <= GetRight();
594 }
595 
596 
IsInsideItalicRect(const Point & rPoint) const597 sal_Bool SmRect::IsInsideItalicRect(const Point &rPoint) const
598 {
599 	return	   rPoint.Y() >= GetTop()
600 		   &&  rPoint.Y() <= GetBottom()
601 		   &&  rPoint.X() >= GetItalicLeft()
602 		   &&  rPoint.X() <= GetItalicRight();
603 }
604 
AsGlyphRect() const605 SmRect SmRect::AsGlyphRect() const
606 {
607 	SmRect aRect (*this);
608 	aRect.SetTop(nGlyphTop);
609 	aRect.SetBottom(nGlyphBottom);
610 	return aRect;
611 }
612 
613 #ifdef SM_RECT_DEBUG
614 
615 // forward declaration
616 void SmDrawFrame(OutputDevice &rDev, const Rectangle &rRec,
617 				 const Color aCol = COL_BLACK);
618 
Draw(OutputDevice & rDev,const Point & rPosition,int nFlags) const619 void SmRect::Draw(OutputDevice &rDev, const Point &rPosition, int nFlags) const
620 {
621 	if (IsEmpty())
622 		return;
623 
624 	rDev.Push(PUSH_LINECOLOR);
625 
626 	if (nFlags & SM_RECT_LINES)
627 	{	long   nLeftSpace  = 0,
628 			   nRightSpace = 0;
629 
630 		if (nFlags & SM_RECT_ITALIC)
631 		{	nLeftSpace	= GetItalicLeftSpace();
632 			nRightSpace = GetItalicRightSpace();
633 		}
634 
635 		long  nLeft  = GetLeft()  - nLeftSpace,
636 			  nRight = GetRight() + nRightSpace;
637 
638 		Point aOffset (rPosition - GetTopLeft());
639 
640 		rDev.SetLineColor(COL_LIGHTBLUE);
641 		rDev.DrawLine(Point(nLeft,	GetAlignB()) += aOffset,
642 					  Point(nRight, GetAlignB()) += aOffset);
643 		rDev.DrawLine(Point(nLeft,	GetAlignT()) += aOffset,
644 					  Point(nRight, GetAlignT()) += aOffset);
645 		if (HasBaseline())
646 			rDev.DrawLine(Point(nLeft,	GetBaseline()) += aOffset,
647 						  Point(nRight, GetBaseline()) += aOffset);
648 
649 		rDev.SetLineColor(COL_GRAY);
650 		rDev.DrawLine(Point(nLeft,	GetHiAttrFence()) += aOffset,
651 					  Point(nRight, GetHiAttrFence()) += aOffset);
652 	}
653 
654 	if (nFlags & SM_RECT_MID)
655 	{	Point	aCenter = rPosition
656 						  + (Point(GetItalicCenterX(), GetAlignM()) -= GetTopLeft()),
657 				aLenX	  (GetWidth() / 5, 0),
658 				aLenY	  (0, GetHeight() / 16);
659 
660 		rDev.SetLineColor(COL_LIGHTGREEN);
661 		rDev.DrawLine(aCenter - aLenX, aCenter + aLenX);
662 		rDev.DrawLine(aCenter - aLenY, aCenter + aLenY);
663 	}
664 
665 	if (nFlags & SM_RECT_ITALIC)
666 		SmDrawFrame(rDev, Rectangle(rPosition - Point(GetItalicLeftSpace(), 0),
667 				GetItalicSize()));
668 
669 	if (nFlags & SM_RECT_CORE)
670 		SmDrawFrame(rDev, Rectangle(rPosition, GetSize()), COL_LIGHTRED);
671 
672 	rDev.Pop();
673 }
674 
675 
SmDrawFrame(OutputDevice & rDev,const Rectangle & rRec,const Color aCol)676 void SmDrawFrame(OutputDevice &rDev, const Rectangle &rRec,
677 				 const Color aCol)
678 {
679 	rDev.Push(PUSH_LINECOLOR);
680 
681 	rDev.SetLineColor(aCol);
682 
683 	rDev.DrawLine(rRec.TopLeft(),	  rRec.BottomLeft());
684 	rDev.DrawLine(rRec.BottomLeft(),  rRec.BottomRight());
685 	rDev.DrawLine(rRec.BottomRight(), rRec.TopRight());
686 	rDev.DrawLine(rRec.TopRight(),	  rRec.TopLeft());
687 
688 	rDev.Pop();
689 }
690 
691 #endif //SM_RECT_DEBUG
692 
693 
SmGetGlyphBoundRect(const OutputDevice & rDev,const XubString & rText,Rectangle & rRect)694 sal_Bool SmGetGlyphBoundRect(const OutputDevice &rDev,
695 						 const XubString &rText, Rectangle &rRect)
696     // basically the same as 'GetTextBoundRect' (in class 'OutputDevice')
697 	// but with a string as argument.
698 {
699 	// handle special case first
700 	xub_StrLen nLen = rText.Len();
701 	if (nLen == 0)
702 	{	rRect.SetEmpty();
703 		return sal_True;
704 	}
705 
706     // get a device where 'OutputDevice::GetTextBoundRect' will be successful
707 	OutputDevice *pGlyphDev;
708 	if (rDev.GetOutDevType() != OUTDEV_PRINTER)
709 		pGlyphDev = (OutputDevice *) &rDev;
710 	else
711 	{
712         // since we format for the printer (where GetTextBoundRect will fail)
713 		// we need a virtual device here.
714         pGlyphDev = &SM_MOD()->GetDefaultVirtualDev();
715 	}
716 
717 	const FontMetric  aDevFM (rDev.GetFontMetric());
718 
719     pGlyphDev->Push(PUSH_FONT | PUSH_MAPMODE);
720     Font aFnt(rDev.GetFont());
721     aFnt.SetAlign(ALIGN_TOP);
722 
723     // use scale factor when calling GetTextBoundRect to counter
724     // negative effects from antialiasing which may otherwise result
725     // in significant incorrect bounding rectangles for some charcters.
726 	Size aFntSize = aFnt.GetSize();
727 
728     // HDU: workaround to avoid HUGE font sizes and resulting problems (#112783#)
729     long nScaleFactor = 1;
730     while( aFntSize.Height() > 2000 * nScaleFactor )
731         nScaleFactor *= 2;
732 
733     aFnt.SetSize( Size( aFntSize.Width() / nScaleFactor, aFntSize.Height() / nScaleFactor ) );
734 	pGlyphDev->SetFont(aFnt);
735 
736     long nTextWidth = rDev.GetTextWidth(rText);
737     Point aPoint;
738     Rectangle   aResult (aPoint, Size(nTextWidth, rDev.GetTextHeight())),
739 				aTmp;
740 
741     sal_Bool bSuccess = pGlyphDev->GetTextBoundRect(aTmp, rText, 0, 0);
742     DBG_ASSERT( bSuccess, "GetTextBoundRect failed" );
743 
744 
745     if (!aTmp.IsEmpty())
746     {
747         aResult = Rectangle(aTmp.Left() * nScaleFactor, aTmp.Top() * nScaleFactor,
748                             aTmp.Right() * nScaleFactor, aTmp.Bottom() * nScaleFactor);
749         if (&rDev != pGlyphDev) /* only when rDev is a printer... */
750         {
751             long nGDTextWidth  = pGlyphDev->GetTextWidth(rText);
752             if (nGDTextWidth != 0  &&
753                 nTextWidth != nGDTextWidth)
754             {
755                 aResult.Right() *= nTextWidth;
756                 aResult.Right() /= nGDTextWidth * nScaleFactor;
757             }
758         }
759     }
760 
761 	// move rectangle to match possibly different baselines
762 	// (because of different devices)
763     long nDelta = aDevFM.GetAscent() - pGlyphDev->GetFontMetric().GetAscent() * nScaleFactor;
764 	aResult.Move(0, nDelta);
765 
766 	pGlyphDev->Pop();
767 
768     rRect = aResult;
769 	return bSuccess;
770 }
771 
772 
773