Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "nsMathMLFrame.h"
7 :
8 : #include "gfxContext.h"
9 : #include "gfxUtils.h"
10 : #include "mozilla/gfx/2D.h"
11 : #include "nsLayoutUtils.h"
12 : #include "nsNameSpaceManager.h"
13 : #include "nsMathMLChar.h"
14 : #include "nsCSSPseudoElements.h"
15 : #include "nsMathMLElement.h"
16 : #include "gfxMathTable.h"
17 :
18 : // used to map attributes into CSS rules
19 : #include "mozilla/StyleSetHandle.h"
20 : #include "mozilla/StyleSetHandleInlines.h"
21 : #include "nsDisplayList.h"
22 :
23 : using namespace mozilla;
24 : using namespace mozilla::gfx;
25 :
26 : eMathMLFrameType
27 0 : nsMathMLFrame::GetMathMLFrameType()
28 : {
29 : // see if it is an embellished operator (mapped to 'Op' in TeX)
30 0 : if (mEmbellishData.coreFrame)
31 0 : return GetMathMLFrameTypeFor(mEmbellishData.coreFrame);
32 :
33 : // if it has a prescribed base, fetch the type from there
34 0 : if (mPresentationData.baseFrame)
35 0 : return GetMathMLFrameTypeFor(mPresentationData.baseFrame);
36 :
37 : // everything else is treated as ordinary (mapped to 'Ord' in TeX)
38 0 : return eMathMLFrameType_Ordinary;
39 : }
40 :
41 : NS_IMETHODIMP
42 0 : nsMathMLFrame::InheritAutomaticData(nsIFrame* aParent)
43 : {
44 0 : mEmbellishData.flags = 0;
45 0 : mEmbellishData.coreFrame = nullptr;
46 0 : mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;
47 0 : mEmbellishData.leadingSpace = 0;
48 0 : mEmbellishData.trailingSpace = 0;
49 :
50 0 : mPresentationData.flags = 0;
51 0 : mPresentationData.baseFrame = nullptr;
52 :
53 : // by default, just inherit the display of our parent
54 0 : nsPresentationData parentData;
55 0 : GetPresentationDataFrom(aParent, parentData);
56 :
57 : #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
58 : mPresentationData.flags |= NS_MATHML_SHOW_BOUNDING_METRICS;
59 : #endif
60 :
61 0 : return NS_OK;
62 : }
63 :
64 : NS_IMETHODIMP
65 0 : nsMathMLFrame::UpdatePresentationData(uint32_t aFlagsValues,
66 : uint32_t aWhichFlags)
67 : {
68 0 : NS_ASSERTION(NS_MATHML_IS_COMPRESSED(aWhichFlags) ||
69 : NS_MATHML_IS_DTLS_SET(aWhichFlags),
70 : "aWhichFlags should only be compression or dtls flag");
71 :
72 0 : if (NS_MATHML_IS_COMPRESSED(aWhichFlags)) {
73 : // updating the compression flag is allowed
74 0 : if (NS_MATHML_IS_COMPRESSED(aFlagsValues)) {
75 : // 'compressed' means 'prime' style in App. G, TeXbook
76 0 : mPresentationData.flags |= NS_MATHML_COMPRESSED;
77 : }
78 : // no else. the flag is sticky. it retains its value once it is set
79 : }
80 : // These flags determine whether the dtls font feature settings should
81 : // be applied.
82 0 : if (NS_MATHML_IS_DTLS_SET(aWhichFlags)) {
83 0 : if (NS_MATHML_IS_DTLS_SET(aFlagsValues)) {
84 0 : mPresentationData.flags |= NS_MATHML_DTLS;
85 : } else {
86 0 : mPresentationData.flags &= ~NS_MATHML_DTLS;
87 : }
88 : }
89 0 : return NS_OK;
90 : }
91 :
92 : // Helper to give a style context suitable for doing the stretching of
93 : // a MathMLChar. Frame classes that use this should ensure that the
94 : // extra leaf style contexts given to the MathMLChars are accessible to
95 : // the Style System via the Get/Set AdditionalStyleContext() APIs.
96 : /* static */ void
97 0 : nsMathMLFrame::ResolveMathMLCharStyle(nsPresContext* aPresContext,
98 : nsIContent* aContent,
99 : nsStyleContext* aParentStyleContext,
100 : nsMathMLChar* aMathMLChar)
101 : {
102 : CSSPseudoElementType pseudoType =
103 0 : CSSPseudoElementType::mozMathAnonymous; // savings
104 0 : RefPtr<nsStyleContext> newStyleContext;
105 0 : newStyleContext = aPresContext->StyleSet()->
106 0 : ResolvePseudoElementStyle(aContent->AsElement(), pseudoType,
107 0 : aParentStyleContext, nullptr);
108 :
109 0 : aMathMLChar->SetStyleContext(newStyleContext);
110 0 : }
111 :
112 : /* static */ void
113 0 : nsMathMLFrame::GetEmbellishDataFrom(nsIFrame* aFrame,
114 : nsEmbellishData& aEmbellishData)
115 : {
116 : // initialize OUT params
117 0 : aEmbellishData.flags = 0;
118 0 : aEmbellishData.coreFrame = nullptr;
119 0 : aEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;
120 0 : aEmbellishData.leadingSpace = 0;
121 0 : aEmbellishData.trailingSpace = 0;
122 :
123 0 : if (aFrame && aFrame->IsFrameOfType(nsIFrame::eMathML)) {
124 0 : nsIMathMLFrame* mathMLFrame = do_QueryFrame(aFrame);
125 0 : if (mathMLFrame) {
126 0 : mathMLFrame->GetEmbellishData(aEmbellishData);
127 : }
128 : }
129 0 : }
130 :
131 : // helper to get the presentation data of a frame, by possibly walking up
132 : // the frame hierarchy if we happen to be surrounded by non-MathML frames.
133 : /* static */ void
134 0 : nsMathMLFrame::GetPresentationDataFrom(nsIFrame* aFrame,
135 : nsPresentationData& aPresentationData,
136 : bool aClimbTree)
137 : {
138 : // initialize OUT params
139 0 : aPresentationData.flags = 0;
140 0 : aPresentationData.baseFrame = nullptr;
141 :
142 0 : nsIFrame* frame = aFrame;
143 0 : while (frame) {
144 0 : if (frame->IsFrameOfType(nsIFrame::eMathML)) {
145 0 : nsIMathMLFrame* mathMLFrame = do_QueryFrame(frame);
146 0 : if (mathMLFrame) {
147 0 : mathMLFrame->GetPresentationData(aPresentationData);
148 0 : break;
149 : }
150 : }
151 : // stop if the caller doesn't want to lookup beyond the frame
152 0 : if (!aClimbTree) {
153 0 : break;
154 : }
155 : // stop if we reach the root <math> tag
156 0 : nsIContent* content = frame->GetContent();
157 0 : NS_ASSERTION(content || !frame->GetParent(), // no assert for the root
158 : "dangling frame without a content node");
159 0 : if (!content)
160 0 : break;
161 :
162 0 : if (content->IsMathMLElement(nsGkAtoms::math)) {
163 0 : break;
164 : }
165 0 : frame = frame->GetParent();
166 : }
167 0 : NS_WARNING_ASSERTION(
168 : frame && frame->GetContent(),
169 : "bad MathML markup - could not find the top <math> element");
170 0 : }
171 :
172 : /* static */ void
173 0 : nsMathMLFrame::GetRuleThickness(DrawTarget* aDrawTarget,
174 : nsFontMetrics* aFontMetrics,
175 : nscoord& aRuleThickness)
176 : {
177 0 : nscoord xHeight = aFontMetrics->XHeight();
178 0 : char16_t overBar = 0x00AF;
179 : nsBoundingMetrics bm =
180 : nsLayoutUtils::AppUnitBoundsOfString(&overBar, 1, *aFontMetrics,
181 0 : aDrawTarget);
182 0 : aRuleThickness = bm.ascent + bm.descent;
183 0 : if (aRuleThickness <= 0 || aRuleThickness >= xHeight) {
184 : // fall-back to the other version
185 0 : GetRuleThickness(aFontMetrics, aRuleThickness);
186 : }
187 0 : }
188 :
189 : /* static */ void
190 0 : nsMathMLFrame::GetAxisHeight(DrawTarget* aDrawTarget,
191 : nsFontMetrics* aFontMetrics,
192 : nscoord& aAxisHeight)
193 : {
194 0 : gfxFont* mathFont = aFontMetrics->GetThebesFontGroup()->GetFirstMathFont();
195 0 : if (mathFont) {
196 0 : aAxisHeight =
197 0 : mathFont->MathTable()->Constant(gfxMathTable::AxisHeight,
198 0 : aFontMetrics->AppUnitsPerDevPixel());
199 0 : return;
200 : }
201 :
202 0 : nscoord xHeight = aFontMetrics->XHeight();
203 0 : char16_t minus = 0x2212; // not '-', but official Unicode minus sign
204 : nsBoundingMetrics bm =
205 0 : nsLayoutUtils::AppUnitBoundsOfString(&minus, 1, *aFontMetrics, aDrawTarget);
206 0 : aAxisHeight = bm.ascent - (bm.ascent + bm.descent)/2;
207 0 : if (aAxisHeight <= 0 || aAxisHeight >= xHeight) {
208 : // fall-back to the other version
209 0 : GetAxisHeight(aFontMetrics, aAxisHeight);
210 : }
211 : }
212 :
213 : /* static */ nscoord
214 0 : nsMathMLFrame::CalcLength(nsPresContext* aPresContext,
215 : nsStyleContext* aStyleContext,
216 : const nsCSSValue& aCSSValue,
217 : float aFontSizeInflation)
218 : {
219 0 : NS_ASSERTION(aCSSValue.IsLengthUnit(), "not a length unit");
220 :
221 0 : if (aCSSValue.IsFixedLengthUnit()) {
222 0 : return aCSSValue.GetFixedLength(aPresContext);
223 : }
224 0 : if (aCSSValue.IsPixelLengthUnit()) {
225 0 : return aCSSValue.GetPixelLength();
226 : }
227 :
228 0 : nsCSSUnit unit = aCSSValue.GetUnit();
229 :
230 0 : if (eCSSUnit_EM == unit) {
231 0 : const nsStyleFont* font = aStyleContext->StyleFont();
232 0 : return NSToCoordRound(aCSSValue.GetFloatValue() * (float)font->mFont.size);
233 : }
234 0 : else if (eCSSUnit_XHeight == unit) {
235 0 : aPresContext->SetUsesExChUnits(true);
236 : RefPtr<nsFontMetrics> fm = nsLayoutUtils::
237 0 : GetFontMetricsForStyleContext(aStyleContext, aFontSizeInflation);
238 0 : nscoord xHeight = fm->XHeight();
239 0 : return NSToCoordRound(aCSSValue.GetFloatValue() * (float)xHeight);
240 : }
241 :
242 : // MathML doesn't specify other CSS units such as rem or ch
243 0 : NS_ERROR("Unsupported unit");
244 0 : return 0;
245 : }
246 :
247 : /* static */ void
248 0 : nsMathMLFrame::ParseNumericValue(const nsString& aString,
249 : nscoord* aLengthValue,
250 : uint32_t aFlags,
251 : nsPresContext* aPresContext,
252 : nsStyleContext* aStyleContext,
253 : float aFontSizeInflation)
254 : {
255 0 : nsCSSValue cssValue;
256 :
257 0 : if (!nsMathMLElement::ParseNumericValue(aString, cssValue, aFlags,
258 : aPresContext->Document())) {
259 : // Invalid attribute value. aLengthValue remains unchanged, so the default
260 : // length value is used.
261 0 : return;
262 : }
263 :
264 0 : nsCSSUnit unit = cssValue.GetUnit();
265 :
266 0 : if (unit == eCSSUnit_Percent || unit == eCSSUnit_Number) {
267 : // Relative units. A multiple of the default length value is used.
268 0 : *aLengthValue = NSToCoordRound(*aLengthValue * (unit == eCSSUnit_Percent ?
269 : cssValue.GetPercentValue() :
270 : cssValue.GetFloatValue()));
271 0 : return;
272 : }
273 :
274 : // Absolute units.
275 0 : *aLengthValue = CalcLength(aPresContext, aStyleContext, cssValue,
276 : aFontSizeInflation);
277 : }
278 :
279 : #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
280 : class nsDisplayMathMLBoundingMetrics : public nsDisplayItem {
281 : public:
282 : nsDisplayMathMLBoundingMetrics(nsDisplayListBuilder* aBuilder,
283 : nsIFrame* aFrame, const nsRect& aRect)
284 : : nsDisplayItem(aBuilder, aFrame), mRect(aRect) {
285 : MOZ_COUNT_CTOR(nsDisplayMathMLBoundingMetrics);
286 : }
287 : #ifdef NS_BUILD_REFCNT_LOGGING
288 : virtual ~nsDisplayMathMLBoundingMetrics() {
289 : MOZ_COUNT_DTOR(nsDisplayMathMLBoundingMetrics);
290 : }
291 : #endif
292 :
293 : virtual void Paint(nsDisplayListBuilder* aBuilder,
294 : gfxContext* aCtx) override;
295 : NS_DISPLAY_DECL_NAME("MathMLBoundingMetrics", TYPE_MATHML_BOUNDING_METRICS)
296 : private:
297 : nsRect mRect;
298 : };
299 :
300 : void nsDisplayMathMLBoundingMetrics::Paint(nsDisplayListBuilder* aBuilder,
301 : gfxContext* aCtx)
302 : {
303 : DrawTarget* drawTarget = aCtx->GetDrawTarget();
304 : Rect r = NSRectToRect(mRect + ToReferenceFrame(),
305 : mFrame->PresContext()->AppUnitsPerDevPixel());
306 : ColorPattern blue(ToDeviceColor(Color(0.f, 0.f, 1.f, 1.f)));
307 : drawTarget->StrokeRect(r, blue);
308 : }
309 :
310 : void
311 : nsMathMLFrame::DisplayBoundingMetrics(nsDisplayListBuilder* aBuilder,
312 : nsIFrame* aFrame, const nsPoint& aPt,
313 : const nsBoundingMetrics& aMetrics,
314 : const nsDisplayListSet& aLists) {
315 : if (!NS_MATHML_PAINT_BOUNDING_METRICS(mPresentationData.flags))
316 : return;
317 :
318 : nscoord x = aPt.x + aMetrics.leftBearing;
319 : nscoord y = aPt.y - aMetrics.ascent;
320 : nscoord w = aMetrics.rightBearing - aMetrics.leftBearing;
321 : nscoord h = aMetrics.ascent + aMetrics.descent;
322 :
323 : aLists.Content()->AppendNewToTop(new (aBuilder)
324 : nsDisplayMathMLBoundingMetrics(aBuilder, aFrame, nsRect(x,y,w,h)));
325 : }
326 : #endif
327 :
328 : class nsDisplayMathMLBar : public nsDisplayItem {
329 : public:
330 0 : nsDisplayMathMLBar(nsDisplayListBuilder* aBuilder,
331 : nsIFrame* aFrame, const nsRect& aRect)
332 0 : : nsDisplayItem(aBuilder, aFrame), mRect(aRect) {
333 0 : MOZ_COUNT_CTOR(nsDisplayMathMLBar);
334 0 : }
335 : #ifdef NS_BUILD_REFCNT_LOGGING
336 0 : virtual ~nsDisplayMathMLBar() {
337 0 : MOZ_COUNT_DTOR(nsDisplayMathMLBar);
338 0 : }
339 : #endif
340 :
341 : virtual void Paint(nsDisplayListBuilder* aBuilder,
342 : gfxContext* aCtx) override;
343 0 : NS_DISPLAY_DECL_NAME("MathMLBar", TYPE_MATHML_BAR)
344 : private:
345 : nsRect mRect;
346 : };
347 :
348 0 : void nsDisplayMathMLBar::Paint(nsDisplayListBuilder* aBuilder,
349 : gfxContext* aCtx)
350 : {
351 : // paint the bar with the current text color
352 0 : DrawTarget* drawTarget = aCtx->GetDrawTarget();
353 : Rect rect =
354 0 : NSRectToNonEmptySnappedRect(mRect + ToReferenceFrame(),
355 0 : mFrame->PresContext()->AppUnitsPerDevPixel(),
356 0 : *drawTarget);
357 0 : ColorPattern color(ToDeviceColor(
358 0 : mFrame->GetVisitedDependentColor(&nsStyleText::mWebkitTextFillColor)));
359 0 : drawTarget->FillRect(rect, color);
360 0 : }
361 :
362 : void
363 0 : nsMathMLFrame::DisplayBar(nsDisplayListBuilder* aBuilder,
364 : nsIFrame* aFrame, const nsRect& aRect,
365 : const nsDisplayListSet& aLists) {
366 0 : if (!aFrame->StyleVisibility()->IsVisible() || aRect.IsEmpty())
367 0 : return;
368 :
369 0 : aLists.Content()->AppendNewToTop(new (aBuilder)
370 0 : nsDisplayMathMLBar(aBuilder, aFrame, aRect));
371 : }
372 :
373 : void
374 0 : nsMathMLFrame::GetRadicalParameters(nsFontMetrics* aFontMetrics,
375 : bool aDisplayStyle,
376 : nscoord& aRadicalRuleThickness,
377 : nscoord& aRadicalExtraAscender,
378 : nscoord& aRadicalVerticalGap)
379 : {
380 0 : nscoord oneDevPixel = aFontMetrics->AppUnitsPerDevPixel();
381 0 : gfxFont* mathFont = aFontMetrics->GetThebesFontGroup()->GetFirstMathFont();
382 :
383 : // get the radical rulethickness
384 0 : if (mathFont) {
385 0 : aRadicalRuleThickness = mathFont->MathTable()->
386 0 : Constant(gfxMathTable::RadicalRuleThickness, oneDevPixel);
387 : } else {
388 0 : GetRuleThickness(aFontMetrics, aRadicalRuleThickness);
389 : }
390 :
391 : // get the leading to be left at the top of the resulting frame
392 0 : if (mathFont) {
393 0 : aRadicalExtraAscender = mathFont->MathTable()->
394 0 : Constant(gfxMathTable::RadicalExtraAscender, oneDevPixel);
395 : } else {
396 : // This seems more reliable than using aFontMetrics->GetLeading() on
397 : // suspicious fonts.
398 : nscoord em;
399 0 : GetEmHeight(aFontMetrics, em);
400 0 : aRadicalExtraAscender = nscoord(0.2f * em);
401 : }
402 :
403 : // get the clearance between rule and content
404 0 : if (mathFont) {
405 0 : aRadicalVerticalGap = mathFont->MathTable()->
406 0 : Constant(aDisplayStyle ?
407 : gfxMathTable::RadicalDisplayStyleVerticalGap :
408 : gfxMathTable::RadicalVerticalGap,
409 : oneDevPixel);
410 : } else {
411 : // Rule 11, App. G, TeXbook
412 0 : aRadicalVerticalGap = aRadicalRuleThickness +
413 0 : (aDisplayStyle ? aFontMetrics->XHeight() : aRadicalRuleThickness) / 4;
414 : }
415 0 : }
|