Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  nsMathMLmrootFrame.cpp   Sprache: unbekannt

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */


#include "nsMathMLmrootFrame.h"

#include "mozilla/PresShell.h"
#include "nsLayoutUtils.h"
#include "nsPresContext.h"
#include <algorithm>
#include "gfxContext.h"
#include "gfxMathTable.h"

using namespace mozilla;

//
// <mroot> -- form a radical - implementation
//

static const char16_t kSqrChar = char16_t(0x221A);

nsIFrame* NS_NewMathMLmrootFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
  return new (aPresShell)
      nsMathMLmrootFrame(aStyle, aPresShell->GetPresContext());
}

NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmrootFrame)

nsMathMLmrootFrame::nsMathMLmrootFrame(ComputedStyle* aStyle,
                                       nsPresContext* aPresContext)
    : nsMathMLContainerFrame(aStyle, aPresContext, kClassID) {}

nsMathMLmrootFrame::~nsMathMLmrootFrame() = default;

void nsMathMLmrootFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
                              nsIFrame* aPrevInFlow) {
  nsMathMLContainerFrame::Init(aContent, aParent, aPrevInFlow);

  nsAutoString sqrChar;
  sqrChar.Assign(kSqrChar);
  mSqrChar.SetData(sqrChar);
  mSqrChar.SetComputedStyle(Style());
}

bool nsMathMLmrootFrame::ShouldUseRowFallback() {
  bool isRootWithIndex = GetContent()->IsMathMLElement(nsGkAtoms::mroot_);
  if (!isRootWithIndex) {
    return false;
  }
  // An mroot element expects exactly two children.
  nsIFrame* baseFrame = mFrames.FirstChild();
  if (!baseFrame) {
    return true;
  }
  nsIFrame* indexFrame = baseFrame->GetNextSibling();
  return !indexFrame || indexFrame->GetNextSibling();
}

bool nsMathMLmrootFrame::IsMrowLike() {
  bool isRootWithIndex = GetContent()->IsMathMLElement(nsGkAtoms::mroot_);
  if (isRootWithIndex) {
    return false;
  }
  return mFrames.FirstChild() != mFrames.LastChild() || !mFrames.FirstChild();
}

NS_IMETHODIMP
nsMathMLmrootFrame::InheritAutomaticData(nsIFrame* aParent) {
  nsMathMLContainerFrame::InheritAutomaticData(aParent);

  bool isRootWithIndex = GetContent()->IsMathMLElement(nsGkAtoms::mroot_);
  if (!isRootWithIndex) {
    mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY;
  }

  return NS_OK;
}

NS_IMETHODIMP
nsMathMLmrootFrame::TransmitAutomaticData() {
  bool isRootWithIndex = GetContent()->IsMathMLElement(nsGkAtoms::mroot_);
  if (isRootWithIndex) {
    // 1. The REC says:
    //    The <mroot> element increments scriptlevel by 2, and sets displaystyle
    //    to "false", within index, but leaves both attributes unchanged within
    //    base.
    // 2. The TeXbook (Ch 17. p.141) says \sqrt is compressed
    UpdatePresentationDataFromChildAt(1, 1, NS_MATHML_COMPRESSED,
                                      NS_MATHML_COMPRESSED);
    UpdatePresentationDataFromChildAt(0, 0, NS_MATHML_COMPRESSED,
                                      NS_MATHML_COMPRESSED);

    PropagateFrameFlagFor(mFrames.LastChild(),
                          NS_FRAME_MATHML_SCRIPT_DESCENDANT);
  } else {
    // The TeXBook (Ch 17. p.141) says that \sqrt is cramped
    UpdatePresentationDataFromChildAt(0, -1, NS_MATHML_COMPRESSED,
                                      NS_MATHML_COMPRESSED);
  }

  return NS_OK;
}

void nsMathMLmrootFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
                                          const nsDisplayListSet& aLists) {
  /////////////
  // paint the content we are square-rooting
  nsMathMLContainerFrame::BuildDisplayList(aBuilder, aLists);

  if (ShouldUseRowFallback()) {
    return;
  }

  /////////////
  // paint the sqrt symbol
  mSqrChar.Display(aBuilder, this, aLists, 0);

  DisplayBar(aBuilder, this, mBarRect, aLists);
}

void nsMathMLmrootFrame::GetRadicalXOffsets(nscoord aIndexWidth,
                                            nscoord aSqrWidth,
                                            nsFontMetrics* aFontMetrics,
                                            nscoord* aIndexOffset,
                                            nscoord* aSqrOffset) {
  // The index is tucked in closer to the radical while making sure
  // that the kern does not make the index and radical collide
  nscoord dxIndex, dxSqr, radicalKernBeforeDegree, radicalKernAfterDegree;
  nscoord oneDevPixel = aFontMetrics->AppUnitsPerDevPixel();
  RefPtr<gfxFont> mathFont =
      aFontMetrics->GetThebesFontGroup()->GetFirstMathFont();

  if (mathFont) {
    radicalKernBeforeDegree = mathFont->MathTable()->Constant(
        gfxMathTable::RadicalKernBeforeDegree, oneDevPixel);
    radicalKernAfterDegree = mathFont->MathTable()->Constant(
        gfxMathTable::RadicalKernAfterDegree, oneDevPixel);
  } else {
    nscoord em;
    GetEmHeight(aFontMetrics, em);
    radicalKernBeforeDegree = NSToCoordRound(5.0f * em / 18);
    radicalKernAfterDegree = NSToCoordRound(-10.0f * em / 18);
  }

  // Clamp radical kern degrees according to spec:
  // https://w3c.github.io/mathml-core/#root-with-index
  radicalKernBeforeDegree = std::max(0, radicalKernBeforeDegree);
  radicalKernAfterDegree = std::max(-aIndexWidth, radicalKernAfterDegree);

  dxIndex = radicalKernBeforeDegree;
  dxSqr = radicalKernBeforeDegree + aIndexWidth + radicalKernAfterDegree;
  if (aIndexOffset) {
    *aIndexOffset = dxIndex;
  }
  if (aSqrOffset) {
    *aSqrOffset = dxSqr;
  }
}

nsresult nsMathMLmrootFrame::Place(DrawTarget* aDrawTarget,
                                   const PlaceFlags& aFlags,
                                   ReflowOutput& aDesiredSize) {
  if (ShouldUseRowFallback()) {
    // report an error, encourage people to get their markups in order
    if (!aFlags.contains(PlaceFlag::MeasureOnly)) {
      ReportChildCountError();
    }
    return PlaceAsMrow(aDrawTarget, aFlags, aDesiredSize);
  }

  const bool isRootWithIndex = GetContent()->IsMathMLElement(nsGkAtoms::mroot_);
  nsBoundingMetrics bmSqr, bmBase, bmIndex;
  nsIFrame *baseFrame = nullptr, *indexFrame = nullptr;
  nsMargin baseMargin, indexMargin;
  ReflowOutput baseSize(aDesiredSize.GetWritingMode());
  ReflowOutput indexSize(aDesiredSize.GetWritingMode());
  if (isRootWithIndex) {
    baseFrame = mFrames.FirstChild();
    indexFrame = baseFrame->GetNextSibling();
    baseMargin = GetMarginForPlace(aFlags, baseFrame);
    indexMargin = GetMarginForPlace(aFlags, indexFrame);
    GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase);
    GetReflowAndBoundingMetricsFor(indexFrame, indexSize, bmIndex);
  } else {
    // Format our content as an mrow without border/padding to obtain the
    // square root base. The metrics/frame for the index are ignored.
    PlaceFlags flags = aFlags + PlaceFlag::MeasureOnly +
                       PlaceFlag::IgnoreBorderPadding +
                       PlaceFlag::DoNotAdjustForWidthAndHeight;
    nsresult rv = nsMathMLContainerFrame::Place(aDrawTarget, flags, baseSize);
    if (NS_FAILED(rv)) {
      DidReflowChildren(PrincipalChildList().FirstChild());
      return rv;
    }
    bmBase = baseSize.mBoundingMetrics;
  }

  ////////////
  // Prepare the radical symbol and the overline bar

  float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
  RefPtr<nsFontMetrics> fm =
      nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);

  nscoord ruleThickness, leading, psi;
  GetRadicalParameters(fm, StyleFont()->mMathStyle == StyleMathStyle::Normal,
                       ruleThickness, leading, psi);

  // built-in: adjust clearance psi to emulate \mathstrut using '1' (TexBook,
  // p.131)
  char16_t one = '1';
  nsBoundingMetrics bmOne =
      nsLayoutUtils::AppUnitBoundsOfString(&one, 1, *fm, aDrawTarget);
  if (bmOne.ascent > bmBase.ascent + baseMargin.top) {
    psi += bmOne.ascent - bmBase.ascent - baseMargin.top;
  }

  // make sure that the rule appears on on screen
  nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
  if (ruleThickness < onePixel) {
    ruleThickness = onePixel;
  }

  // adjust clearance psi to get an exact number of pixels -- this
  // gives a nicer & uniform look on stacked radicals (bug 130282)
  nscoord delta = psi % onePixel;
  if (delta) {
    psi += onePixel - delta;  // round up
  }

  // Stretch the radical symbol to the appropriate height if it is not big
  // enough.
  nsBoundingMetrics contSize = bmBase;
  contSize.descent =
      bmBase.ascent + bmBase.descent + baseMargin.TopBottom() + psi;
  contSize.ascent = ruleThickness;

  // height(radical) should be >= height(base) + psi + ruleThickness
  nsBoundingMetrics radicalSize;
  if (aFlags.contains(PlaceFlag::IntrinsicSize)) {
    nscoord radical_width =
        mSqrChar.GetMaxWidth(this, aDrawTarget, fontSizeInflation);
    bmSqr.leftBearing = 0;
    bmSqr.rightBearing = radical_width;
    bmSqr.width = radical_width;
    bmSqr.ascent = bmSqr.descent = 0;
  } else {
    mSqrChar.Stretch(this, aDrawTarget, fontSizeInflation,
                     NS_STRETCH_DIRECTION_VERTICAL, contSize, radicalSize,
                     NS_STRETCH_LARGER,
                     StyleVisibility()->mDirection == StyleDirection::Rtl);
    // radicalSize have changed at this point, and should match with
    // the bounding metrics of the char
    mSqrChar.GetBoundingMetrics(bmSqr);
  }

  // Update the desired size for the container (like msqrt, index is not yet
  // included) the baseline will be that of the base.
  mBoundingMetrics.ascent =
      bmBase.ascent + baseMargin.top + psi + ruleThickness;
  mBoundingMetrics.descent =
      std::max(bmBase.descent + baseMargin.bottom,
               (bmSqr.ascent + bmSqr.descent - mBoundingMetrics.ascent));
  mBoundingMetrics.width = bmSqr.width + bmBase.width + baseMargin.LeftRight();
  mBoundingMetrics.leftBearing = bmSqr.leftBearing;
  mBoundingMetrics.rightBearing =
      bmSqr.width +
      std::max(
          bmBase.width + baseMargin.LeftRight(),
          bmBase.rightBearing + baseMargin.left);  // take also care of the rule

  aDesiredSize.SetBlockStartAscent(mBoundingMetrics.ascent + leading);
  aDesiredSize.Height() =
      aDesiredSize.BlockStartAscent() +
      std::max(baseSize.Height() - baseSize.BlockStartAscent(),
               mBoundingMetrics.descent + ruleThickness);
  aDesiredSize.Width() = mBoundingMetrics.width;

  nscoord indexClearance = 0, dxIndex = 0, dxSqr = 0, indexRaisedAscent = 0;
  if (isRootWithIndex) {
    /////////////
    // Re-adjust the desired size to include the index.

    // the index is raised by some fraction of the height
    // of the radical, see \mroot macro in App. B, TexBook
    float raiseIndexPercent = 0.6f;
    RefPtr<gfxFont> mathFont = fm->GetThebesFontGroup()->GetFirstMathFont();
    if (mathFont) {
      raiseIndexPercent = mathFont->MathTable()->Constant(
          gfxMathTable::RadicalDegreeBottomRaisePercent);
    }
    nscoord raiseIndexDelta =
        NSToCoordRound(raiseIndexPercent * (bmSqr.ascent + bmSqr.descent));
    indexRaisedAscent = mBoundingMetrics.ascent  // top of radical
                        -
                        (bmSqr.ascent + bmSqr.descent)  // to bottom of radical
                        + raiseIndexDelta + bmIndex.ascent + bmIndex.descent +
                        indexMargin.TopBottom();  // to top of raised index

    if (mBoundingMetrics.ascent < indexRaisedAscent) {
      indexClearance =
          indexRaisedAscent -
          mBoundingMetrics.ascent;  // excess gap introduced by a tall index
      mBoundingMetrics.ascent = indexRaisedAscent;
      nscoord descent = aDesiredSize.Height() - aDesiredSize.BlockStartAscent();
      aDesiredSize.SetBlockStartAscent(mBoundingMetrics.ascent + leading);
      aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + descent;
    }

    GetRadicalXOffsets(bmIndex.width + indexMargin.LeftRight(), bmSqr.width, fm,
                       &dxIndex, &dxSqr);

    mBoundingMetrics.width =
        dxSqr + bmSqr.width + bmBase.width + baseMargin.LeftRight();
    mBoundingMetrics.leftBearing =
        std::min(dxIndex + bmIndex.leftBearing, dxSqr + bmSqr.leftBearing);
    mBoundingMetrics.rightBearing =
        dxSqr + bmSqr.width +
        std::max(bmBase.width + baseMargin.LeftRight(),
                 bmBase.rightBearing + baseMargin.left);

    aDesiredSize.Width() = mBoundingMetrics.width;
  }

  aDesiredSize.mBoundingMetrics = mBoundingMetrics;

  // Apply width/height to math content box.
  const PlaceFlags flags;
  auto sizes = GetWidthAndHeightForPlaceAdjustment(flags);
  nscoord shiftX = ApplyAdjustmentForWidthAndHeight(flags, sizes, aDesiredSize,
                                                    mBoundingMetrics);

  // Add padding+border around the final layout.
  auto borderPadding = GetBorderPaddingForPlace(aFlags);
  InflateReflowAndBoundingMetrics(borderPadding, aDesiredSize,
                                  mBoundingMetrics);

  if (!aFlags.contains(PlaceFlag::MeasureOnly)) {
    nsPresContext* presContext = PresContext();
    const bool isRTL = StyleVisibility()->mDirection == StyleDirection::Rtl;
    nscoord borderPaddingInlineStart =
        isRTL ? borderPadding.right : borderPadding.left;
    nscoord dx, dy;

    if (isRootWithIndex) {
      // place the index
      dx = borderPaddingInlineStart + dxIndex +
           indexMargin.Side(isRTL ? eSideRight : eSideLeft);
      dy = aDesiredSize.BlockStartAscent() -
           (indexRaisedAscent + indexSize.BlockStartAscent() - bmIndex.ascent);
      FinishReflowChild(
          indexFrame, presContext, indexSize, nullptr,
          MirrorIfRTL(aDesiredSize.Width(), indexSize.Width(), dx),
          dy + indexMargin.top, ReflowChildFlags::Default);
    }

    // place the radical symbol and the radical bar
    dx = borderPaddingInlineStart + dxSqr;
    dy = borderPadding.top + indexClearance +
         leading;  // leave a leading at the top
    mSqrChar.SetRect(nsRect(MirrorIfRTL(aDesiredSize.Width(), bmSqr.width, dx),
                            dy, bmSqr.width, bmSqr.ascent + bmSqr.descent));
    dx += bmSqr.width;
    mBarRect.SetRect(MirrorIfRTL(aDesiredSize.Width(),
                                 bmBase.width + baseMargin.LeftRight(), dx),
                     dy, bmBase.width + baseMargin.LeftRight(), ruleThickness);

    // place the base
    if (isRootWithIndex) {
      dx += isRTL ? baseMargin.right : baseMargin.left;
      dy = aDesiredSize.BlockStartAscent() - baseSize.BlockStartAscent();
      FinishReflowChild(baseFrame, presContext, baseSize, nullptr,
                        MirrorIfRTL(aDesiredSize.Width(), baseSize.Width(), dx),
                        dy, ReflowChildFlags::Default);
    } else {
      nscoord dx_left = borderPadding.left + shiftX;
      if (!isRTL) {
        dx_left += bmSqr.width;
      }
      PositionRowChildFrames(dx_left, aDesiredSize.BlockStartAscent());
    }
  }

  mReference.x = 0;
  mReference.y = aDesiredSize.BlockStartAscent();

  return NS_OK;
}

void nsMathMLmrootFrame::DidSetComputedStyle(ComputedStyle* aOldStyle) {
  nsMathMLContainerFrame::DidSetComputedStyle(aOldStyle);
  mSqrChar.SetComputedStyle(Style());
}

95%


[ zur Elbe Produktseite wechseln0.18Quellennavigators  Analyse erneut starten  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge