eecdaa02b6
Change-Id: I39cfbe43e7f27ada9999daf93aa6ccfd38fb0c52 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/176561 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
621 lines
20 KiB
C++
621 lines
20 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
#include <osl/diagnose.h>
|
|
#include <o3tl/sorted_vector.hxx>
|
|
#include <vcl/metric.hxx>
|
|
#include <vcl/svapp.hxx>
|
|
#include <vcl/virdev.hxx>
|
|
#include <sal/log.hxx>
|
|
|
|
#include <format.hxx>
|
|
#include <rect.hxx>
|
|
#include <types.hxx>
|
|
#include <smmod.hxx>
|
|
|
|
namespace {
|
|
|
|
bool SmGetGlyphBoundRect(const vcl::RenderContext &rDev,
|
|
const OUString &rText, tools::Rectangle &rRect)
|
|
// basically the same as 'GetTextBoundRect' (in class 'OutputDevice')
|
|
// but with a string as argument.
|
|
{
|
|
// handle special case first
|
|
if (rText.isEmpty())
|
|
{
|
|
rRect.SetEmpty();
|
|
return true;
|
|
}
|
|
|
|
// get a device where 'OutputDevice::GetTextBoundRect' will be successful
|
|
OutputDevice *pGlyphDev;
|
|
if (rDev.GetOutDevType() != OUTDEV_PRINTER)
|
|
pGlyphDev = const_cast<OutputDevice *>(&rDev);
|
|
else
|
|
{
|
|
// since we format for the printer (where GetTextBoundRect will fail)
|
|
// we need a virtual device here.
|
|
pGlyphDev = &SmModule::get()->GetDefaultVirtualDev();
|
|
}
|
|
|
|
const FontMetric aDevFM (rDev.GetFontMetric());
|
|
|
|
pGlyphDev->Push(vcl::PushFlags::FONT | vcl::PushFlags::MAPMODE);
|
|
vcl::Font aFnt(rDev.GetFont());
|
|
aFnt.SetAlignment(ALIGN_TOP);
|
|
|
|
// use scale factor when calling GetTextBoundRect to counter
|
|
// negative effects from antialiasing which may otherwise result
|
|
// in significant incorrect bounding rectangles for some characters.
|
|
Size aFntSize = aFnt.GetFontSize();
|
|
|
|
// Workaround to avoid HUGE font sizes and resulting problems
|
|
tools::Long nScaleFactor = 1;
|
|
while( aFntSize.Height() > 2000 * nScaleFactor )
|
|
nScaleFactor *= 2;
|
|
|
|
aFnt.SetFontSize( Size( aFntSize.Width() / nScaleFactor, aFntSize.Height() / nScaleFactor ) );
|
|
pGlyphDev->SetFont(aFnt);
|
|
|
|
tools::Long nTextWidth = rDev.GetTextWidth(rText);
|
|
tools::Rectangle aResult (Point(), Size(nTextWidth, rDev.GetTextHeight())),
|
|
aTmp;
|
|
|
|
bool bSuccess = pGlyphDev->GetTextBoundRect(aTmp, rText);
|
|
OSL_ENSURE( bSuccess, "GetTextBoundRect failed" );
|
|
|
|
|
|
if (!aTmp.IsEmpty())
|
|
{
|
|
aResult = tools::Rectangle(aTmp.Left() * nScaleFactor, aTmp.Top() * nScaleFactor,
|
|
aTmp.Right() * nScaleFactor, aTmp.Bottom() * nScaleFactor);
|
|
if (&rDev != pGlyphDev) /* only when rDev is a printer... */
|
|
{
|
|
tools::Long nGDTextWidth = pGlyphDev->GetTextWidth(rText);
|
|
if (nGDTextWidth != 0 &&
|
|
nTextWidth != nGDTextWidth)
|
|
{
|
|
aResult.SetRight( aResult.Right() * nTextWidth );
|
|
aResult.SetRight( aResult.Right() / ( nGDTextWidth * nScaleFactor) );
|
|
}
|
|
}
|
|
}
|
|
|
|
// move rectangle to match possibly different baselines
|
|
// (because of different devices)
|
|
tools::Long nDelta = aDevFM.GetAscent() - pGlyphDev->GetFontMetric().GetAscent() * nScaleFactor;
|
|
aResult.Move(0, nDelta);
|
|
|
|
pGlyphDev->Pop();
|
|
|
|
rRect = aResult;
|
|
return bSuccess;
|
|
}
|
|
|
|
bool SmIsMathAlpha(std::u16string_view aText)
|
|
// true iff symbol (from StarMath Font) should be treated as letter
|
|
{
|
|
// Set of symbols, which should be treated as letters in StarMath Font
|
|
// (to get a normal (non-clipped) SmRect in contrast to the other operators
|
|
// and symbols).
|
|
static o3tl::sorted_vector<sal_Unicode> const aMathAlpha({
|
|
MS_ALEPH, MS_IM, MS_RE,
|
|
MS_WP, u'\xE070', MS_EMPTYSET,
|
|
u'\x2113', u'\xE0D6', u'\x2107',
|
|
u'\x2127', u'\x210A', MS_HBAR,
|
|
MS_LAMBDABAR, MS_SETN, MS_SETZ,
|
|
MS_SETQ, MS_SETR, MS_SETC,
|
|
u'\x2373', u'\xE0A5', u'\x2112',
|
|
u'\x2130', u'\x2131'
|
|
});
|
|
|
|
if (aText.empty())
|
|
return false;
|
|
|
|
OSL_ENSURE(aText.size() == 1, "Sm : string must be exactly one character long");
|
|
sal_Unicode cChar = aText[0];
|
|
|
|
// is it a greek symbol?
|
|
if (u'\xE0AC' <= cChar && cChar <= u'\xE0D4')
|
|
return true;
|
|
// or, does it appear in 'aMathAlpha'?
|
|
return aMathAlpha.find(cChar) != aMathAlpha.end();
|
|
}
|
|
|
|
}
|
|
|
|
|
|
SmRect::SmRect()
|
|
// constructs empty rectangle at (0, 0) with width and height 0.
|
|
: aTopLeft(0, 0)
|
|
, aSize(0, 0)
|
|
, nBaseline(0)
|
|
, nAlignT(0)
|
|
, nAlignM(0)
|
|
, nAlignB(0)
|
|
, nGlyphTop(0)
|
|
, nGlyphBottom(0)
|
|
, nItalicLeftSpace(0)
|
|
, nItalicRightSpace(0)
|
|
, nLoAttrFence(0)
|
|
, nHiAttrFence(0)
|
|
, nBorderWidth(0)
|
|
, bHasBaseline(false)
|
|
, bHasAlignInfo(false)
|
|
{
|
|
}
|
|
|
|
|
|
void SmRect::CopyAlignInfo(const SmRect &rRect)
|
|
{
|
|
nBaseline = rRect.nBaseline;
|
|
bHasBaseline = rRect.bHasBaseline;
|
|
nAlignT = rRect.nAlignT;
|
|
nAlignM = rRect.nAlignM;
|
|
nAlignB = rRect.nAlignB;
|
|
bHasAlignInfo = rRect.bHasAlignInfo;
|
|
nLoAttrFence = rRect.nLoAttrFence;
|
|
nHiAttrFence = rRect.nHiAttrFence;
|
|
}
|
|
|
|
|
|
SmRect::SmRect(const OutputDevice &rDev, const SmFormat *pFormat,
|
|
const OUString &rText, sal_uInt16 nBorder)
|
|
// get rectangle fitting for drawing 'rText' on OutputDevice 'rDev'
|
|
: aTopLeft(0, 0)
|
|
, aSize(rDev.GetTextWidth(rText), rDev.GetTextHeight())
|
|
{
|
|
const FontMetric aFM (rDev.GetFontMetric());
|
|
bool bIsMath = aFM.GetFamilyName().equalsIgnoreAsciiCase( FONTNAME_MATH );
|
|
bool bAllowSmaller = bIsMath && !SmIsMathAlpha(rText);
|
|
const tools::Long nFontHeight = rDev.GetFont().GetFontSize().Height();
|
|
|
|
nBorderWidth = nBorder;
|
|
bHasAlignInfo = true;
|
|
bHasBaseline = true;
|
|
nBaseline = aFM.GetAscent();
|
|
nAlignT = nBaseline - nFontHeight * 750 / 1000;
|
|
nAlignM = nBaseline - nFontHeight * 121 / 422;
|
|
// that's where the horizontal bars of '+', '-', ... are
|
|
// (1/3 of ascent over baseline)
|
|
// (121 = 1/3 of 12pt ascent, 422 = 12pt fontheight)
|
|
nAlignB = nBaseline;
|
|
|
|
// workaround for printer fonts with very small (possible 0 or even
|
|
// negative(!)) leading
|
|
if (aFM.GetInternalLeading() < 5 && rDev.GetOutDevType() == OUTDEV_PRINTER)
|
|
{
|
|
OutputDevice *pWindow = Application::GetDefaultDevice();
|
|
|
|
pWindow->Push(vcl::PushFlags::MAPMODE | vcl::PushFlags::FONT);
|
|
|
|
pWindow->SetMapMode(rDev.GetMapMode());
|
|
pWindow->SetFont(rDev.GetFontMetric());
|
|
|
|
tools::Long nDelta = pWindow->GetFontMetric().GetInternalLeading();
|
|
if (nDelta == 0)
|
|
{ // this value approx. fits a Leading of 80 at a
|
|
// Fontheight of 422 (12pt)
|
|
nDelta = nFontHeight * 8 / 43;
|
|
}
|
|
SetTop(GetTop() - nDelta);
|
|
|
|
pWindow->Pop();
|
|
}
|
|
|
|
// get GlyphBoundRect
|
|
tools::Rectangle aGlyphRect;
|
|
bool bSuccess = SmGetGlyphBoundRect(rDev, rText, aGlyphRect);
|
|
if (!bSuccess)
|
|
SAL_WARN("starmath", "Ooops... (Font missing?)");
|
|
|
|
nItalicLeftSpace = GetLeft() - aGlyphRect.Left() + nBorderWidth;
|
|
nItalicRightSpace = aGlyphRect.Right() - GetRight() + nBorderWidth;
|
|
if (nItalicLeftSpace < 0 && !bAllowSmaller)
|
|
nItalicLeftSpace = 0;
|
|
if (nItalicRightSpace < 0 && !bAllowSmaller)
|
|
nItalicRightSpace = 0;
|
|
|
|
tools::Long nDist = 0;
|
|
if (pFormat)
|
|
nDist = (rDev.GetFont().GetFontSize().Height()
|
|
* pFormat->GetDistance(DIS_ORNAMENTSIZE)) / 100;
|
|
|
|
nHiAttrFence = aGlyphRect.Top() - 1 - nBorderWidth - nDist;
|
|
nLoAttrFence = SmFromTo(GetAlignB(), GetBottom(), 0.0);
|
|
|
|
nGlyphTop = aGlyphRect.Top() - nBorderWidth;
|
|
nGlyphBottom = aGlyphRect.Bottom() + nBorderWidth;
|
|
|
|
if (bAllowSmaller)
|
|
{
|
|
// for symbols and operators from the StarMath Font
|
|
// we adjust upper and lower margin of the symbol
|
|
SetTop(nGlyphTop);
|
|
SetBottom(nGlyphBottom);
|
|
}
|
|
|
|
if (nHiAttrFence < GetTop())
|
|
nHiAttrFence = GetTop();
|
|
|
|
if (nLoAttrFence > GetBottom())
|
|
nLoAttrFence = GetBottom();
|
|
|
|
OSL_ENSURE(rText.isEmpty() || !IsEmpty(),
|
|
"Sm: empty rectangle created");
|
|
}
|
|
|
|
|
|
SmRect::SmRect(tools::Long nWidth, tools::Long nHeight)
|
|
// this constructor should never be used for anything textlike because
|
|
// it will not provide useful values for baseline, AlignT and AlignB!
|
|
// It's purpose is to get a 'SmRect' for the horizontal line in fractions
|
|
// as used in 'SmBinVerNode'.
|
|
: aTopLeft(0, 0)
|
|
, aSize(nWidth, nHeight)
|
|
, nBaseline(0)
|
|
, nItalicLeftSpace(0)
|
|
, nItalicRightSpace(0)
|
|
, nBorderWidth(0)
|
|
, bHasBaseline(false)
|
|
, bHasAlignInfo(true)
|
|
{
|
|
nAlignT = nGlyphTop = nHiAttrFence = GetTop();
|
|
nAlignB = nGlyphBottom = nLoAttrFence = GetBottom();
|
|
nAlignM = (nAlignT + nAlignB) / 2; // this is the default
|
|
}
|
|
|
|
|
|
void SmRect::SetLeft(tools::Long nLeft)
|
|
{
|
|
if (nLeft <= GetRight())
|
|
{ aSize.setWidth( GetRight() - nLeft + 1 );
|
|
aTopLeft.setX( nLeft );
|
|
}
|
|
}
|
|
|
|
|
|
void SmRect::SetRight(tools::Long nRight)
|
|
{
|
|
if (nRight >= GetLeft())
|
|
aSize.setWidth( nRight - GetLeft() + 1 );
|
|
}
|
|
|
|
|
|
void SmRect::SetBottom(tools::Long nBottom)
|
|
{
|
|
if (nBottom >= GetTop())
|
|
aSize.setHeight( nBottom - GetTop() + 1 );
|
|
}
|
|
|
|
|
|
void SmRect::SetTop(tools::Long nTop)
|
|
{
|
|
if (nTop <= GetBottom())
|
|
{ aSize.setHeight( GetBottom() - nTop + 1 );
|
|
aTopLeft.setY( nTop );
|
|
}
|
|
}
|
|
|
|
|
|
void SmRect::Move(const Point &rPosition)
|
|
// move rectangle by position 'rPosition'.
|
|
{
|
|
aTopLeft += rPosition;
|
|
|
|
tools::Long nDelta = rPosition.Y();
|
|
nBaseline += nDelta;
|
|
nAlignT += nDelta;
|
|
nAlignM += nDelta;
|
|
nAlignB += nDelta;
|
|
nGlyphTop += nDelta;
|
|
nGlyphBottom += nDelta;
|
|
nHiAttrFence += nDelta;
|
|
nLoAttrFence += nDelta;
|
|
}
|
|
|
|
|
|
Point SmRect::AlignTo(const SmRect &rRect, RectPos ePos,
|
|
RectHorAlign eHor, RectVerAlign eVer) const
|
|
{ Point aPos (GetTopLeft());
|
|
// will become the topleft point of the new rectangle position
|
|
|
|
// set horizontal or vertical new rectangle position depending on ePos
|
|
switch (ePos)
|
|
{ case RectPos::Left:
|
|
aPos.setX( rRect.GetItalicLeft() - GetItalicRightSpace()
|
|
- GetWidth() );
|
|
break;
|
|
case RectPos::Right:
|
|
aPos.setX( rRect.GetItalicRight() + 1 + GetItalicLeftSpace() );
|
|
break;
|
|
case RectPos::Top:
|
|
aPos.setY( rRect.GetTop() - GetHeight() );
|
|
break;
|
|
case RectPos::Bottom:
|
|
aPos.setY( rRect.GetBottom() + 1 );
|
|
break;
|
|
case RectPos::Attribute:
|
|
aPos.setX( rRect.GetItalicCenterX() - GetItalicWidth() / 2
|
|
+ GetItalicLeftSpace() );
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
|
|
// check if horizontal position is already set
|
|
if (ePos == RectPos::Left || ePos == RectPos::Right || ePos == RectPos::Attribute)
|
|
// correct error in current vertical position
|
|
switch (eVer)
|
|
{ case RectVerAlign::Top :
|
|
aPos.AdjustY(rRect.GetAlignT() - GetAlignT() );
|
|
break;
|
|
case RectVerAlign::Mid :
|
|
aPos.AdjustY(rRect.GetAlignM() - GetAlignM() );
|
|
break;
|
|
case RectVerAlign::Baseline :
|
|
// align baselines if possible else align mid's
|
|
if (HasBaseline() && rRect.HasBaseline())
|
|
aPos.AdjustY(rRect.GetBaseline() - GetBaseline() );
|
|
else
|
|
aPos.AdjustY(rRect.GetAlignM() - GetAlignM() );
|
|
break;
|
|
case RectVerAlign::Bottom :
|
|
aPos.AdjustY(rRect.GetAlignB() - GetAlignB() );
|
|
break;
|
|
case RectVerAlign::CenterY :
|
|
aPos.AdjustY(rRect.GetCenterY() - GetCenterY() );
|
|
break;
|
|
case RectVerAlign::AttributeHi:
|
|
aPos.AdjustY(rRect.GetHiAttrFence() - GetBottom() );
|
|
break;
|
|
case RectVerAlign::AttributeMid :
|
|
aPos.AdjustY(SmFromTo(rRect.GetAlignB(), rRect.GetAlignT(), 0.4)
|
|
- GetCenterY() );
|
|
break;
|
|
case RectVerAlign::AttributeLo :
|
|
aPos.AdjustY(rRect.GetLoAttrFence() - GetTop() );
|
|
break;
|
|
default :
|
|
assert(false);
|
|
}
|
|
|
|
// check if vertical position is already set
|
|
if (ePos == RectPos::Top || ePos == RectPos::Bottom)
|
|
// correct error in current horizontal position
|
|
switch (eHor)
|
|
{ case RectHorAlign::Left:
|
|
aPos.AdjustX(rRect.GetItalicLeft() - GetItalicLeft() );
|
|
break;
|
|
case RectHorAlign::Center:
|
|
aPos.AdjustX(rRect.GetItalicCenterX() - GetItalicCenterX() );
|
|
break;
|
|
case RectHorAlign::Right:
|
|
aPos.AdjustX(rRect.GetItalicRight() - GetItalicRight() );
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
|
|
return aPos;
|
|
}
|
|
|
|
|
|
void SmRect::Union(const SmRect &rRect)
|
|
// rectangle union of current one with 'rRect'. The result is to be the
|
|
// smallest rectangles that covers the space of both rectangles.
|
|
// (empty rectangles cover no space)
|
|
//! Italic correction is NOT taken into account here!
|
|
{
|
|
if (rRect.IsEmpty())
|
|
return;
|
|
|
|
tools::Long nL = rRect.GetLeft(),
|
|
nR = rRect.GetRight(),
|
|
nT = rRect.GetTop(),
|
|
nB = rRect.GetBottom(),
|
|
nGT = rRect.nGlyphTop,
|
|
nGB = rRect.nGlyphBottom;
|
|
if (!IsEmpty())
|
|
{ tools::Long nTmp;
|
|
|
|
if ((nTmp = GetLeft()) < nL)
|
|
nL = nTmp;
|
|
if ((nTmp = GetRight()) > nR)
|
|
nR = nTmp;
|
|
if ((nTmp = GetTop()) < nT)
|
|
nT = nTmp;
|
|
if ((nTmp = GetBottom()) > nB)
|
|
nB = nTmp;
|
|
if ((nTmp = nGlyphTop) < nGT)
|
|
nGT = nTmp;
|
|
if ((nTmp = nGlyphBottom) > nGB)
|
|
nGB = nTmp;
|
|
}
|
|
|
|
SetLeft(nL);
|
|
SetRight(nR);
|
|
SetTop(nT);
|
|
SetBottom(nB);
|
|
nGlyphTop = nGT;
|
|
nGlyphBottom = nGB;
|
|
}
|
|
|
|
|
|
SmRect & SmRect::ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode)
|
|
// let current rectangle be the union of itself and 'rRect'
|
|
// (the smallest rectangle surrounding both). Also adapt values for
|
|
// 'AlignT', 'AlignM', 'AlignB', baseline and italic-spaces.
|
|
// The baseline is set according to 'eCopyMode'.
|
|
// If one of the rectangles has no relevant info the other one is copied.
|
|
{
|
|
// get some values used for (italic) spaces adaptation
|
|
// ! (need to be done before changing current SmRect) !
|
|
tools::Long nL = std::min(GetItalicLeft(), rRect.GetItalicLeft()),
|
|
nR = std::max(GetItalicRight(), rRect.GetItalicRight());
|
|
|
|
Union(rRect);
|
|
|
|
SetItalicSpaces(GetLeft() - nL, nR - GetRight());
|
|
|
|
if (!HasAlignInfo())
|
|
CopyAlignInfo(rRect);
|
|
else if (rRect.HasAlignInfo())
|
|
{
|
|
assert(HasAlignInfo());
|
|
nAlignT = std::min(GetAlignT(), rRect.GetAlignT());
|
|
nAlignB = std::max(GetAlignB(), rRect.GetAlignB());
|
|
nHiAttrFence = std::min(GetHiAttrFence(), rRect.GetHiAttrFence());
|
|
nLoAttrFence = std::max(GetLoAttrFence(), rRect.GetLoAttrFence());
|
|
|
|
switch (eCopyMode)
|
|
{ case RectCopyMBL::This:
|
|
// already done
|
|
break;
|
|
case RectCopyMBL::Arg:
|
|
CopyMBL(rRect);
|
|
break;
|
|
case RectCopyMBL::None:
|
|
bHasBaseline = false;
|
|
nAlignM = (nAlignT + nAlignB) / 2;
|
|
break;
|
|
case RectCopyMBL::Xor:
|
|
if (!HasBaseline())
|
|
CopyMBL(rRect);
|
|
break;
|
|
default :
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
void SmRect::ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode,
|
|
tools::Long nNewAlignM)
|
|
// as 'ExtendBy' but sets AlignM value to 'nNewAlignM'.
|
|
// (this version will be used in 'SmBinVerNode' to provide means to
|
|
// align eg "{a over b} over c" correctly where AlignM should not
|
|
// be (AlignT + AlignB) / 2)
|
|
{
|
|
OSL_ENSURE(HasAlignInfo(), "Sm: no align info");
|
|
|
|
ExtendBy(rRect, eCopyMode);
|
|
nAlignM = nNewAlignM;
|
|
}
|
|
|
|
|
|
SmRect & SmRect::ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode,
|
|
bool bKeepVerAlignParams)
|
|
// as 'ExtendBy' but keeps original values for AlignT, -M and -B and
|
|
// baseline.
|
|
// (this is used in 'SmSupSubNode' where the sub-/supscripts shouldn't
|
|
// be allowed to modify these values.)
|
|
{
|
|
tools::Long nOldAlignT = GetAlignT(),
|
|
nOldAlignM = GetAlignM(),
|
|
nOldAlignB = GetAlignB(),
|
|
nOldBaseline = nBaseline; //! depends not on 'HasBaseline'
|
|
bool bOldHasAlignInfo = HasAlignInfo();
|
|
|
|
ExtendBy(rRect, eCopyMode);
|
|
|
|
if (bKeepVerAlignParams)
|
|
{ nAlignT = nOldAlignT;
|
|
nAlignM = nOldAlignM;
|
|
nAlignB = nOldAlignB;
|
|
nBaseline = nOldBaseline;
|
|
bHasAlignInfo = bOldHasAlignInfo;
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
tools::Long SmRect::OrientedDist(const Point &rPoint) const
|
|
// return oriented distance of rPoint to the current rectangle,
|
|
// especially the return value is <= 0 iff the point is inside the
|
|
// rectangle.
|
|
// For simplicity the maximum-norm is used.
|
|
{
|
|
bool bIsInside = IsInsideItalicRect(rPoint);
|
|
|
|
// build reference point to define the distance
|
|
Point aRef;
|
|
if (bIsInside)
|
|
{ Point aIC (GetItalicCenterX(), GetCenterY());
|
|
|
|
aRef.setX( rPoint.X() >= aIC.X() ? GetItalicRight() : GetItalicLeft() );
|
|
aRef.setY( rPoint.Y() >= aIC.Y() ? GetBottom() : GetTop() );
|
|
}
|
|
else
|
|
{
|
|
// x-coordinate
|
|
if (rPoint.X() > GetItalicRight())
|
|
aRef.setX( GetItalicRight() );
|
|
else if (rPoint.X() < GetItalicLeft())
|
|
aRef.setX( GetItalicLeft() );
|
|
else
|
|
aRef.setX( rPoint.X() );
|
|
// y-coordinate
|
|
if (rPoint.Y() > GetBottom())
|
|
aRef.setY( GetBottom() );
|
|
else if (rPoint.Y() < GetTop())
|
|
aRef.setY( GetTop() );
|
|
else
|
|
aRef.setY( rPoint.Y() );
|
|
}
|
|
|
|
// build distance vector
|
|
Point aDist (aRef - rPoint);
|
|
|
|
tools::Long nAbsX = std::abs(aDist.X()),
|
|
nAbsY = std::abs(aDist.Y());
|
|
|
|
return bIsInside ? - std::min(nAbsX, nAbsY) : std::max (nAbsX, nAbsY);
|
|
}
|
|
|
|
|
|
bool SmRect::IsInsideRect(const Point &rPoint) const
|
|
{
|
|
return rPoint.Y() >= GetTop()
|
|
&& rPoint.Y() <= GetBottom()
|
|
&& rPoint.X() >= GetLeft()
|
|
&& rPoint.X() <= GetRight();
|
|
}
|
|
|
|
|
|
bool SmRect::IsInsideItalicRect(const Point &rPoint) const
|
|
{
|
|
return rPoint.Y() >= GetTop()
|
|
&& rPoint.Y() <= GetBottom()
|
|
&& rPoint.X() >= GetItalicLeft()
|
|
&& rPoint.X() <= GetItalicRight();
|
|
}
|
|
|
|
SmRect SmRect::AsGlyphRect() const
|
|
{
|
|
SmRect aRect (*this);
|
|
aRect.SetTop(nGlyphTop);
|
|
aRect.SetBottom(nGlyphBottom);
|
|
return aRect;
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|