Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/starmath/source/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 19 kB image not shown  

Quelle  rect.cxx   Sprache: 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: */

Messung V0.5
C=95 H=97 G=95

¤ Dauer der Verarbeitung: 0.7 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.