123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428 |
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* 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 "nsMathMLFrame.h"
- #include "gfxUtils.h"
- #include "mozilla/gfx/2D.h"
- #include "nsLayoutUtils.h"
- #include "nsNameSpaceManager.h"
- #include "nsMathMLChar.h"
- #include "nsCSSPseudoElements.h"
- #include "nsMathMLElement.h"
- #include "gfxMathTable.h"
- // used to map attributes into CSS rules
- #include "mozilla/StyleSetHandle.h"
- #include "mozilla/StyleSetHandleInlines.h"
- #include "nsDisplayList.h"
- #include "nsRenderingContext.h"
- using namespace mozilla;
- using namespace mozilla::gfx;
- eMathMLFrameType
- nsMathMLFrame::GetMathMLFrameType()
- {
- // see if it is an embellished operator (mapped to 'Op' in TeX)
- if (mEmbellishData.coreFrame)
- return GetMathMLFrameTypeFor(mEmbellishData.coreFrame);
- // if it has a prescribed base, fetch the type from there
- if (mPresentationData.baseFrame)
- return GetMathMLFrameTypeFor(mPresentationData.baseFrame);
- // everything else is treated as ordinary (mapped to 'Ord' in TeX)
- return eMathMLFrameType_Ordinary;
- }
- NS_IMETHODIMP
- nsMathMLFrame::InheritAutomaticData(nsIFrame* aParent)
- {
- mEmbellishData.flags = 0;
- mEmbellishData.coreFrame = nullptr;
- mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;
- mEmbellishData.leadingSpace = 0;
- mEmbellishData.trailingSpace = 0;
- mPresentationData.flags = 0;
- mPresentationData.baseFrame = nullptr;
- // by default, just inherit the display of our parent
- nsPresentationData parentData;
- GetPresentationDataFrom(aParent, parentData);
- #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
- mPresentationData.flags |= NS_MATHML_SHOW_BOUNDING_METRICS;
- #endif
- return NS_OK;
- }
- NS_IMETHODIMP
- nsMathMLFrame::UpdatePresentationData(uint32_t aFlagsValues,
- uint32_t aWhichFlags)
- {
- NS_ASSERTION(NS_MATHML_IS_COMPRESSED(aWhichFlags) ||
- NS_MATHML_IS_DTLS_SET(aWhichFlags),
- "aWhichFlags should only be compression or dtls flag");
- if (NS_MATHML_IS_COMPRESSED(aWhichFlags)) {
- // updating the compression flag is allowed
- if (NS_MATHML_IS_COMPRESSED(aFlagsValues)) {
- // 'compressed' means 'prime' style in App. G, TeXbook
- mPresentationData.flags |= NS_MATHML_COMPRESSED;
- }
- // no else. the flag is sticky. it retains its value once it is set
- }
- // These flags determine whether the dtls font feature settings should
- // be applied.
- if (NS_MATHML_IS_DTLS_SET(aWhichFlags)) {
- if (NS_MATHML_IS_DTLS_SET(aFlagsValues)) {
- mPresentationData.flags |= NS_MATHML_DTLS;
- } else {
- mPresentationData.flags &= ~NS_MATHML_DTLS;
- }
- }
- return NS_OK;
- }
- // Helper to give a style context suitable for doing the stretching of
- // a MathMLChar. Frame classes that use this should ensure that the
- // extra leaf style contexts given to the MathMLChars are accessible to
- // the Style System via the Get/Set AdditionalStyleContext() APIs.
- /* static */ void
- nsMathMLFrame::ResolveMathMLCharStyle(nsPresContext* aPresContext,
- nsIContent* aContent,
- nsStyleContext* aParentStyleContext,
- nsMathMLChar* aMathMLChar)
- {
- CSSPseudoElementType pseudoType =
- CSSPseudoElementType::mozMathAnonymous; // savings
- RefPtr<nsStyleContext> newStyleContext;
- newStyleContext = aPresContext->StyleSet()->
- ResolvePseudoElementStyle(aContent->AsElement(), pseudoType,
- aParentStyleContext, nullptr);
- aMathMLChar->SetStyleContext(newStyleContext);
- }
- /* static */ void
- nsMathMLFrame::GetEmbellishDataFrom(nsIFrame* aFrame,
- nsEmbellishData& aEmbellishData)
- {
- // initialize OUT params
- aEmbellishData.flags = 0;
- aEmbellishData.coreFrame = nullptr;
- aEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;
- aEmbellishData.leadingSpace = 0;
- aEmbellishData.trailingSpace = 0;
- if (aFrame && aFrame->IsFrameOfType(nsIFrame::eMathML)) {
- nsIMathMLFrame* mathMLFrame = do_QueryFrame(aFrame);
- if (mathMLFrame) {
- mathMLFrame->GetEmbellishData(aEmbellishData);
- }
- }
- }
- // helper to get the presentation data of a frame, by possibly walking up
- // the frame hierarchy if we happen to be surrounded by non-MathML frames.
- /* static */ void
- nsMathMLFrame::GetPresentationDataFrom(nsIFrame* aFrame,
- nsPresentationData& aPresentationData,
- bool aClimbTree)
- {
- // initialize OUT params
- aPresentationData.flags = 0;
- aPresentationData.baseFrame = nullptr;
- nsIFrame* frame = aFrame;
- while (frame) {
- if (frame->IsFrameOfType(nsIFrame::eMathML)) {
- nsIMathMLFrame* mathMLFrame = do_QueryFrame(frame);
- if (mathMLFrame) {
- mathMLFrame->GetPresentationData(aPresentationData);
- break;
- }
- }
- // stop if the caller doesn't want to lookup beyond the frame
- if (!aClimbTree) {
- break;
- }
- // stop if we reach the root <math> tag
- nsIContent* content = frame->GetContent();
- NS_ASSERTION(content || !frame->GetParent(), // no assert for the root
- "dangling frame without a content node");
- if (!content)
- break;
- if (content->IsMathMLElement(nsGkAtoms::math)) {
- break;
- }
- frame = frame->GetParent();
- }
- NS_WARNING_ASSERTION(
- frame && frame->GetContent(),
- "bad MathML markup - could not find the top <math> element");
- }
- /* static */ void
- nsMathMLFrame::GetRuleThickness(DrawTarget* aDrawTarget,
- nsFontMetrics* aFontMetrics,
- nscoord& aRuleThickness)
- {
- nscoord xHeight = aFontMetrics->XHeight();
- char16_t overBar = 0x00AF;
- nsBoundingMetrics bm =
- nsLayoutUtils::AppUnitBoundsOfString(&overBar, 1, *aFontMetrics,
- aDrawTarget);
- aRuleThickness = bm.ascent + bm.descent;
- if (aRuleThickness <= 0 || aRuleThickness >= xHeight) {
- // fall-back to the other version
- GetRuleThickness(aFontMetrics, aRuleThickness);
- }
- }
- /* static */ void
- nsMathMLFrame::GetAxisHeight(DrawTarget* aDrawTarget,
- nsFontMetrics* aFontMetrics,
- nscoord& aAxisHeight)
- {
- gfxFont* mathFont = aFontMetrics->GetThebesFontGroup()->GetFirstMathFont();
- if (mathFont) {
- aAxisHeight =
- mathFont->MathTable()->Constant(gfxMathTable::AxisHeight,
- aFontMetrics->AppUnitsPerDevPixel());
- return;
- }
- nscoord xHeight = aFontMetrics->XHeight();
- char16_t minus = 0x2212; // not '-', but official Unicode minus sign
- nsBoundingMetrics bm =
- nsLayoutUtils::AppUnitBoundsOfString(&minus, 1, *aFontMetrics, aDrawTarget);
- aAxisHeight = bm.ascent - (bm.ascent + bm.descent)/2;
- if (aAxisHeight <= 0 || aAxisHeight >= xHeight) {
- // fall-back to the other version
- GetAxisHeight(aFontMetrics, aAxisHeight);
- }
- }
- /* static */ nscoord
- nsMathMLFrame::CalcLength(nsPresContext* aPresContext,
- nsStyleContext* aStyleContext,
- const nsCSSValue& aCSSValue,
- float aFontSizeInflation)
- {
- NS_ASSERTION(aCSSValue.IsLengthUnit(), "not a length unit");
- if (aCSSValue.IsFixedLengthUnit()) {
- return aCSSValue.GetFixedLength(aPresContext);
- }
- if (aCSSValue.IsPixelLengthUnit()) {
- return aCSSValue.GetPixelLength();
- }
- nsCSSUnit unit = aCSSValue.GetUnit();
- if (eCSSUnit_EM == unit) {
- const nsStyleFont* font = aStyleContext->StyleFont();
- return NSToCoordRound(aCSSValue.GetFloatValue() * (float)font->mFont.size);
- }
- else if (eCSSUnit_XHeight == unit) {
- aPresContext->SetUsesExChUnits(true);
- RefPtr<nsFontMetrics> fm = nsLayoutUtils::
- GetFontMetricsForStyleContext(aStyleContext, aFontSizeInflation);
- nscoord xHeight = fm->XHeight();
- return NSToCoordRound(aCSSValue.GetFloatValue() * (float)xHeight);
- }
- // MathML doesn't specify other CSS units such as rem or ch
- NS_ERROR("Unsupported unit");
- return 0;
- }
- /* static */ void
- nsMathMLFrame::ParseNumericValue(const nsString& aString,
- nscoord* aLengthValue,
- uint32_t aFlags,
- nsPresContext* aPresContext,
- nsStyleContext* aStyleContext,
- float aFontSizeInflation)
- {
- nsCSSValue cssValue;
- if (!nsMathMLElement::ParseNumericValue(aString, cssValue, aFlags,
- aPresContext->Document())) {
- // Invalid attribute value. aLengthValue remains unchanged, so the default
- // length value is used.
- return;
- }
- nsCSSUnit unit = cssValue.GetUnit();
- if (unit == eCSSUnit_Percent || unit == eCSSUnit_Number) {
- // Relative units. A multiple of the default length value is used.
- *aLengthValue = NSToCoordRound(*aLengthValue * (unit == eCSSUnit_Percent ?
- cssValue.GetPercentValue() :
- cssValue.GetFloatValue()));
- return;
- }
-
- // Absolute units.
- *aLengthValue = CalcLength(aPresContext, aStyleContext, cssValue,
- aFontSizeInflation);
- }
- // ================
- // Utils to map attributes into CSS rules (work-around to bug 69409 which
- // is not scheduled to be fixed anytime soon)
- //
- struct
- nsCSSMapping {
- int32_t compatibility;
- const nsIAtom* attrAtom;
- const char* cssProperty;
- };
- #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
- class nsDisplayMathMLBoundingMetrics : public nsDisplayItem {
- public:
- nsDisplayMathMLBoundingMetrics(nsDisplayListBuilder* aBuilder,
- nsIFrame* aFrame, const nsRect& aRect)
- : nsDisplayItem(aBuilder, aFrame), mRect(aRect) {
- MOZ_COUNT_CTOR(nsDisplayMathMLBoundingMetrics);
- }
- #ifdef NS_BUILD_REFCNT_LOGGING
- virtual ~nsDisplayMathMLBoundingMetrics() {
- MOZ_COUNT_DTOR(nsDisplayMathMLBoundingMetrics);
- }
- #endif
- virtual void Paint(nsDisplayListBuilder* aBuilder,
- nsRenderingContext* aCtx) override;
- NS_DISPLAY_DECL_NAME("MathMLBoundingMetrics", TYPE_MATHML_BOUNDING_METRICS)
- private:
- nsRect mRect;
- };
- void nsDisplayMathMLBoundingMetrics::Paint(nsDisplayListBuilder* aBuilder,
- nsRenderingContext* aCtx)
- {
- DrawTarget* drawTarget = aCtx->GetDrawTarget();
- Rect r = NSRectToRect(mRect + ToReferenceFrame(),
- mFrame->PresContext()->AppUnitsPerDevPixel());
- ColorPattern blue(ToDeviceColor(Color(0.f, 0.f, 1.f, 1.f)));
- drawTarget->StrokeRect(r, blue);
- }
- void
- nsMathMLFrame::DisplayBoundingMetrics(nsDisplayListBuilder* aBuilder,
- nsIFrame* aFrame, const nsPoint& aPt,
- const nsBoundingMetrics& aMetrics,
- const nsDisplayListSet& aLists) {
- if (!NS_MATHML_PAINT_BOUNDING_METRICS(mPresentationData.flags))
- return;
-
- nscoord x = aPt.x + aMetrics.leftBearing;
- nscoord y = aPt.y - aMetrics.ascent;
- nscoord w = aMetrics.rightBearing - aMetrics.leftBearing;
- nscoord h = aMetrics.ascent + aMetrics.descent;
- aLists.Content()->AppendNewToTop(new (aBuilder)
- nsDisplayMathMLBoundingMetrics(aBuilder, aFrame, nsRect(x,y,w,h)));
- }
- #endif
- class nsDisplayMathMLBar : public nsDisplayItem {
- public:
- nsDisplayMathMLBar(nsDisplayListBuilder* aBuilder,
- nsIFrame* aFrame, const nsRect& aRect)
- : nsDisplayItem(aBuilder, aFrame), mRect(aRect) {
- MOZ_COUNT_CTOR(nsDisplayMathMLBar);
- }
- #ifdef NS_BUILD_REFCNT_LOGGING
- virtual ~nsDisplayMathMLBar() {
- MOZ_COUNT_DTOR(nsDisplayMathMLBar);
- }
- #endif
- virtual void Paint(nsDisplayListBuilder* aBuilder,
- nsRenderingContext* aCtx) override;
- NS_DISPLAY_DECL_NAME("MathMLBar", TYPE_MATHML_BAR)
- private:
- nsRect mRect;
- };
- void nsDisplayMathMLBar::Paint(nsDisplayListBuilder* aBuilder,
- nsRenderingContext* aCtx)
- {
- // paint the bar with the current text color
- DrawTarget* drawTarget = aCtx->GetDrawTarget();
- Rect rect =
- NSRectToNonEmptySnappedRect(mRect + ToReferenceFrame(),
- mFrame->PresContext()->AppUnitsPerDevPixel(),
- *drawTarget);
- ColorPattern color(ToDeviceColor(
- mFrame->GetVisitedDependentColor(eCSSProperty__webkit_text_fill_color)));
- drawTarget->FillRect(rect, color);
- }
- void
- nsMathMLFrame::DisplayBar(nsDisplayListBuilder* aBuilder,
- nsIFrame* aFrame, const nsRect& aRect,
- const nsDisplayListSet& aLists) {
- if (!aFrame->StyleVisibility()->IsVisible() || aRect.IsEmpty())
- return;
- aLists.Content()->AppendNewToTop(new (aBuilder)
- nsDisplayMathMLBar(aBuilder, aFrame, aRect));
- }
- void
- nsMathMLFrame::GetRadicalParameters(nsFontMetrics* aFontMetrics,
- bool aDisplayStyle,
- nscoord& aRadicalRuleThickness,
- nscoord& aRadicalExtraAscender,
- nscoord& aRadicalVerticalGap)
- {
- nscoord oneDevPixel = aFontMetrics->AppUnitsPerDevPixel();
- gfxFont* mathFont = aFontMetrics->GetThebesFontGroup()->GetFirstMathFont();
- // get the radical rulethickness
- if (mathFont) {
- aRadicalRuleThickness = mathFont->MathTable()->
- Constant(gfxMathTable::RadicalRuleThickness, oneDevPixel);
- } else {
- GetRuleThickness(aFontMetrics, aRadicalRuleThickness);
- }
- // get the leading to be left at the top of the resulting frame
- if (mathFont) {
- aRadicalExtraAscender = mathFont->MathTable()->
- Constant(gfxMathTable::RadicalExtraAscender, oneDevPixel);
- } else {
- // This seems more reliable than using aFontMetrics->GetLeading() on
- // suspicious fonts.
- nscoord em;
- GetEmHeight(aFontMetrics, em);
- aRadicalExtraAscender = nscoord(0.2f * em);
- }
- // get the clearance between rule and content
- if (mathFont) {
- aRadicalVerticalGap = mathFont->MathTable()->
- Constant(aDisplayStyle ?
- gfxMathTable::RadicalDisplayStyleVerticalGap :
- gfxMathTable::RadicalVerticalGap,
- oneDevPixel);
- } else {
- // Rule 11, App. G, TeXbook
- aRadicalVerticalGap = aRadicalRuleThickness +
- (aDisplayStyle ? aFontMetrics->XHeight() : aRadicalRuleThickness) / 4;
- }
- }
|