nsMathMLmrootFrame.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  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. #include "nsMathMLmrootFrame.h"
  6. #include "nsPresContext.h"
  7. #include "nsRenderingContext.h"
  8. #include <algorithm>
  9. #include "gfxMathTable.h"
  10. using namespace mozilla;
  11. //
  12. // <mroot> -- form a radical - implementation
  13. //
  14. // additional style context to be used by our MathMLChar.
  15. #define NS_SQR_CHAR_STYLE_CONTEXT_INDEX 0
  16. static const char16_t kSqrChar = char16_t(0x221A);
  17. nsIFrame*
  18. NS_NewMathMLmrootFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
  19. {
  20. return new (aPresShell) nsMathMLmrootFrame(aContext);
  21. }
  22. NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmrootFrame)
  23. nsMathMLmrootFrame::nsMathMLmrootFrame(nsStyleContext* aContext) :
  24. nsMathMLContainerFrame(aContext),
  25. mSqrChar(),
  26. mBarRect()
  27. {
  28. }
  29. nsMathMLmrootFrame::~nsMathMLmrootFrame()
  30. {
  31. }
  32. void
  33. nsMathMLmrootFrame::Init(nsIContent* aContent,
  34. nsContainerFrame* aParent,
  35. nsIFrame* aPrevInFlow)
  36. {
  37. nsMathMLContainerFrame::Init(aContent, aParent, aPrevInFlow);
  38. nsPresContext *presContext = PresContext();
  39. // No need to track the style context given to our MathML char.
  40. // The Style System will use Get/SetAdditionalStyleContext() to keep it
  41. // up-to-date if dynamic changes arise.
  42. nsAutoString sqrChar; sqrChar.Assign(kSqrChar);
  43. mSqrChar.SetData(sqrChar);
  44. ResolveMathMLCharStyle(presContext, mContent, mStyleContext, &mSqrChar);
  45. }
  46. NS_IMETHODIMP
  47. nsMathMLmrootFrame::TransmitAutomaticData()
  48. {
  49. // 1. The REC says:
  50. // The <mroot> element increments scriptlevel by 2, and sets displaystyle to
  51. // "false", within index, but leaves both attributes unchanged within base.
  52. // 2. The TeXbook (Ch 17. p.141) says \sqrt is compressed
  53. UpdatePresentationDataFromChildAt(1, 1,
  54. NS_MATHML_COMPRESSED,
  55. NS_MATHML_COMPRESSED);
  56. UpdatePresentationDataFromChildAt(0, 0,
  57. NS_MATHML_COMPRESSED, NS_MATHML_COMPRESSED);
  58. PropagateFrameFlagFor(mFrames.LastChild(),
  59. NS_FRAME_MATHML_SCRIPT_DESCENDANT);
  60. return NS_OK;
  61. }
  62. void
  63. nsMathMLmrootFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
  64. const nsDisplayListSet& aLists)
  65. {
  66. /////////////
  67. // paint the content we are square-rooting
  68. nsMathMLContainerFrame::BuildDisplayList(aBuilder, aLists);
  69. /////////////
  70. // paint the sqrt symbol
  71. if (!NS_MATHML_HAS_ERROR(mPresentationData.flags)) {
  72. mSqrChar.Display(aBuilder, this, aLists, 0);
  73. DisplayBar(aBuilder, this, mBarRect, aLists);
  74. #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
  75. // for visual debug
  76. nsRect rect;
  77. mSqrChar.GetRect(rect);
  78. nsBoundingMetrics bm;
  79. mSqrChar.GetBoundingMetrics(bm);
  80. DisplayBoundingMetrics(aBuilder, this, rect.TopLeft(), bm, aLists);
  81. #endif
  82. }
  83. }
  84. void
  85. nsMathMLmrootFrame::GetRadicalXOffsets(nscoord aIndexWidth, nscoord aSqrWidth,
  86. nsFontMetrics* aFontMetrics,
  87. nscoord* aIndexOffset,
  88. nscoord* aSqrOffset)
  89. {
  90. // The index is tucked in closer to the radical while making sure
  91. // that the kern does not make the index and radical collide
  92. nscoord dxIndex, dxSqr;
  93. nscoord xHeight = aFontMetrics->XHeight();
  94. nscoord indexRadicalKern = NSToCoordRound(1.35f * xHeight);
  95. nscoord oneDevPixel = aFontMetrics->AppUnitsPerDevPixel();
  96. gfxFont* mathFont = aFontMetrics->GetThebesFontGroup()->GetFirstMathFont();
  97. if (mathFont) {
  98. indexRadicalKern =
  99. mathFont->MathTable()->Constant(gfxMathTable::RadicalKernAfterDegree,
  100. oneDevPixel);
  101. indexRadicalKern = -indexRadicalKern;
  102. }
  103. if (indexRadicalKern > aIndexWidth) {
  104. dxIndex = indexRadicalKern - aIndexWidth;
  105. dxSqr = 0;
  106. }
  107. else {
  108. dxIndex = 0;
  109. dxSqr = aIndexWidth - indexRadicalKern;
  110. }
  111. if (mathFont) {
  112. // add some kern before the radical index
  113. nscoord indexRadicalKernBefore = 0;
  114. indexRadicalKernBefore =
  115. mathFont->MathTable()->Constant(gfxMathTable::RadicalKernBeforeDegree,
  116. oneDevPixel);
  117. dxIndex += indexRadicalKernBefore;
  118. dxSqr += indexRadicalKernBefore;
  119. } else {
  120. // avoid collision by leaving a minimum space between index and radical
  121. nscoord minimumClearance = aSqrWidth / 2;
  122. if (dxIndex + aIndexWidth + minimumClearance > dxSqr + aSqrWidth) {
  123. if (aIndexWidth + minimumClearance < aSqrWidth) {
  124. dxIndex = aSqrWidth - (aIndexWidth + minimumClearance);
  125. dxSqr = 0;
  126. }
  127. else {
  128. dxIndex = 0;
  129. dxSqr = (aIndexWidth + minimumClearance) - aSqrWidth;
  130. }
  131. }
  132. }
  133. if (aIndexOffset)
  134. *aIndexOffset = dxIndex;
  135. if (aSqrOffset)
  136. *aSqrOffset = dxSqr;
  137. }
  138. void
  139. nsMathMLmrootFrame::Reflow(nsPresContext* aPresContext,
  140. ReflowOutput& aDesiredSize,
  141. const ReflowInput& aReflowInput,
  142. nsReflowStatus& aStatus)
  143. {
  144. MarkInReflow();
  145. nsReflowStatus childStatus;
  146. mPresentationData.flags &= ~NS_MATHML_ERROR;
  147. aDesiredSize.ClearSize();
  148. aDesiredSize.SetBlockStartAscent(0);
  149. nsBoundingMetrics bmSqr, bmBase, bmIndex;
  150. DrawTarget* drawTarget = aReflowInput.mRenderingContext->GetDrawTarget();
  151. //////////////////
  152. // Reflow Children
  153. int32_t count = 0;
  154. nsIFrame* baseFrame = nullptr;
  155. nsIFrame* indexFrame = nullptr;
  156. ReflowOutput baseSize(aReflowInput);
  157. ReflowOutput indexSize(aReflowInput);
  158. nsIFrame* childFrame = mFrames.FirstChild();
  159. while (childFrame) {
  160. // ask our children to compute their bounding metrics
  161. ReflowOutput childDesiredSize(aReflowInput,
  162. aDesiredSize.mFlags
  163. | NS_REFLOW_CALC_BOUNDING_METRICS);
  164. WritingMode wm = childFrame->GetWritingMode();
  165. LogicalSize availSize = aReflowInput.ComputedSize(wm);
  166. availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
  167. ReflowInput childReflowInput(aPresContext, aReflowInput,
  168. childFrame, availSize);
  169. ReflowChild(childFrame, aPresContext,
  170. childDesiredSize, childReflowInput, childStatus);
  171. //NS_ASSERTION(NS_FRAME_IS_COMPLETE(childStatus), "bad status");
  172. if (0 == count) {
  173. // base
  174. baseFrame = childFrame;
  175. baseSize = childDesiredSize;
  176. bmBase = childDesiredSize.mBoundingMetrics;
  177. }
  178. else if (1 == count) {
  179. // index
  180. indexFrame = childFrame;
  181. indexSize = childDesiredSize;
  182. bmIndex = childDesiredSize.mBoundingMetrics;
  183. }
  184. count++;
  185. childFrame = childFrame->GetNextSibling();
  186. }
  187. if (2 != count) {
  188. // report an error, encourage people to get their markups in order
  189. ReportChildCountError();
  190. ReflowError(drawTarget, aDesiredSize);
  191. aStatus = NS_FRAME_COMPLETE;
  192. NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
  193. // Call DidReflow() for the child frames we successfully did reflow.
  194. DidReflowChildren(mFrames.FirstChild(), childFrame);
  195. return;
  196. }
  197. ////////////
  198. // Prepare the radical symbol and the overline bar
  199. float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
  200. RefPtr<nsFontMetrics> fm =
  201. nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);
  202. nscoord ruleThickness, leading, psi;
  203. GetRadicalParameters(fm, StyleFont()->mMathDisplay ==
  204. NS_MATHML_DISPLAYSTYLE_BLOCK,
  205. ruleThickness, leading, psi);
  206. // built-in: adjust clearance psi to emulate \mathstrut using '1' (TexBook, p.131)
  207. char16_t one = '1';
  208. nsBoundingMetrics bmOne =
  209. nsLayoutUtils::AppUnitBoundsOfString(&one, 1, *fm, drawTarget);
  210. if (bmOne.ascent > bmBase.ascent)
  211. psi += bmOne.ascent - bmBase.ascent;
  212. // make sure that the rule appears on on screen
  213. nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
  214. if (ruleThickness < onePixel) {
  215. ruleThickness = onePixel;
  216. }
  217. // adjust clearance psi to get an exact number of pixels -- this
  218. // gives a nicer & uniform look on stacked radicals (bug 130282)
  219. nscoord delta = psi % onePixel;
  220. if (delta)
  221. psi += onePixel - delta; // round up
  222. // Stretch the radical symbol to the appropriate height if it is not big enough.
  223. nsBoundingMetrics contSize = bmBase;
  224. contSize.descent = bmBase.ascent + bmBase.descent + psi;
  225. contSize.ascent = ruleThickness;
  226. // height(radical) should be >= height(base) + psi + ruleThickness
  227. nsBoundingMetrics radicalSize;
  228. mSqrChar.Stretch(aPresContext, drawTarget,
  229. fontSizeInflation,
  230. NS_STRETCH_DIRECTION_VERTICAL,
  231. contSize, radicalSize,
  232. NS_STRETCH_LARGER,
  233. StyleVisibility()->mDirection);
  234. // radicalSize have changed at this point, and should match with
  235. // the bounding metrics of the char
  236. mSqrChar.GetBoundingMetrics(bmSqr);
  237. // Update the desired size for the container (like msqrt, index is not yet included)
  238. // the baseline will be that of the base.
  239. mBoundingMetrics.ascent = bmBase.ascent + psi + ruleThickness;
  240. mBoundingMetrics.descent =
  241. std::max(bmBase.descent,
  242. (bmSqr.ascent + bmSqr.descent - mBoundingMetrics.ascent));
  243. mBoundingMetrics.width = bmSqr.width + bmBase.width;
  244. mBoundingMetrics.leftBearing = bmSqr.leftBearing;
  245. mBoundingMetrics.rightBearing = bmSqr.width +
  246. std::max(bmBase.width, bmBase.rightBearing); // take also care of the rule
  247. aDesiredSize.SetBlockStartAscent(mBoundingMetrics.ascent + leading);
  248. aDesiredSize.Height() = aDesiredSize.BlockStartAscent() +
  249. std::max(baseSize.Height() - baseSize.BlockStartAscent(),
  250. mBoundingMetrics.descent + ruleThickness);
  251. aDesiredSize.Width() = mBoundingMetrics.width;
  252. /////////////
  253. // Re-adjust the desired size to include the index.
  254. // the index is raised by some fraction of the height
  255. // of the radical, see \mroot macro in App. B, TexBook
  256. float raiseIndexPercent = 0.6f;
  257. gfxFont* mathFont = fm->GetThebesFontGroup()->GetFirstMathFont();
  258. if (mathFont) {
  259. raiseIndexPercent = mathFont->MathTable()->
  260. Constant(gfxMathTable::RadicalDegreeBottomRaisePercent);
  261. }
  262. nscoord raiseIndexDelta = NSToCoordRound(raiseIndexPercent *
  263. (bmSqr.ascent + bmSqr.descent));
  264. nscoord indexRaisedAscent = mBoundingMetrics.ascent // top of radical
  265. - (bmSqr.ascent + bmSqr.descent) // to bottom of radical
  266. + raiseIndexDelta + bmIndex.ascent + bmIndex.descent; // to top of raised index
  267. nscoord indexClearance = 0;
  268. if (mBoundingMetrics.ascent < indexRaisedAscent) {
  269. indexClearance =
  270. indexRaisedAscent - mBoundingMetrics.ascent; // excess gap introduced by a tall index
  271. mBoundingMetrics.ascent = indexRaisedAscent;
  272. nscoord descent = aDesiredSize.Height() - aDesiredSize.BlockStartAscent();
  273. aDesiredSize.SetBlockStartAscent(mBoundingMetrics.ascent + leading);
  274. aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + descent;
  275. }
  276. nscoord dxIndex, dxSqr;
  277. GetRadicalXOffsets(bmIndex.width, bmSqr.width, fm, &dxIndex, &dxSqr);
  278. mBoundingMetrics.width = dxSqr + bmSqr.width + bmBase.width;
  279. mBoundingMetrics.leftBearing =
  280. std::min(dxIndex + bmIndex.leftBearing, dxSqr + bmSqr.leftBearing);
  281. mBoundingMetrics.rightBearing = dxSqr + bmSqr.width +
  282. std::max(bmBase.width, bmBase.rightBearing);
  283. aDesiredSize.Width() = mBoundingMetrics.width;
  284. aDesiredSize.mBoundingMetrics = mBoundingMetrics;
  285. GatherAndStoreOverflow(&aDesiredSize);
  286. // place the index
  287. nscoord dx = dxIndex;
  288. nscoord dy = aDesiredSize.BlockStartAscent() -
  289. (indexRaisedAscent + indexSize.BlockStartAscent() - bmIndex.ascent);
  290. FinishReflowChild(indexFrame, aPresContext, indexSize, nullptr,
  291. MirrorIfRTL(aDesiredSize.Width(), indexSize.Width(), dx),
  292. dy, 0);
  293. // place the radical symbol and the radical bar
  294. dx = dxSqr;
  295. dy = indexClearance + leading; // leave a leading at the top
  296. mSqrChar.SetRect(nsRect(MirrorIfRTL(aDesiredSize.Width(), bmSqr.width, dx),
  297. dy, bmSqr.width, bmSqr.ascent + bmSqr.descent));
  298. dx += bmSqr.width;
  299. mBarRect.SetRect(MirrorIfRTL(aDesiredSize.Width(), bmBase.width, dx),
  300. dy, bmBase.width, ruleThickness);
  301. // place the base
  302. dy = aDesiredSize.BlockStartAscent() - baseSize.BlockStartAscent();
  303. FinishReflowChild(baseFrame, aPresContext, baseSize, nullptr,
  304. MirrorIfRTL(aDesiredSize.Width(), baseSize.Width(), dx),
  305. dy, 0);
  306. mReference.x = 0;
  307. mReference.y = aDesiredSize.BlockStartAscent();
  308. aStatus = NS_FRAME_COMPLETE;
  309. NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
  310. }
  311. /* virtual */ void
  312. nsMathMLmrootFrame::GetIntrinsicISizeMetrics(nsRenderingContext* aRenderingContext, ReflowOutput& aDesiredSize)
  313. {
  314. nsIFrame* baseFrame = mFrames.FirstChild();
  315. nsIFrame* indexFrame = nullptr;
  316. if (baseFrame)
  317. indexFrame = baseFrame->GetNextSibling();
  318. if (!indexFrame || indexFrame->GetNextSibling()) {
  319. ReflowError(aRenderingContext->GetDrawTarget(), aDesiredSize);
  320. return;
  321. }
  322. float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
  323. nscoord baseWidth =
  324. nsLayoutUtils::IntrinsicForContainer(aRenderingContext, baseFrame,
  325. nsLayoutUtils::PREF_ISIZE);
  326. nscoord indexWidth =
  327. nsLayoutUtils::IntrinsicForContainer(aRenderingContext, indexFrame,
  328. nsLayoutUtils::PREF_ISIZE);
  329. nscoord sqrWidth = mSqrChar.GetMaxWidth(PresContext(),
  330. aRenderingContext->GetDrawTarget(),
  331. fontSizeInflation);
  332. nscoord dxSqr;
  333. RefPtr<nsFontMetrics> fm =
  334. nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);
  335. GetRadicalXOffsets(indexWidth, sqrWidth, fm, nullptr, &dxSqr);
  336. nscoord width = dxSqr + sqrWidth + baseWidth;
  337. aDesiredSize.Width() = width;
  338. aDesiredSize.mBoundingMetrics.width = width;
  339. aDesiredSize.mBoundingMetrics.leftBearing = 0;
  340. aDesiredSize.mBoundingMetrics.rightBearing = width;
  341. }
  342. // ----------------------
  343. // the Style System will use these to pass the proper style context to our MathMLChar
  344. nsStyleContext*
  345. nsMathMLmrootFrame::GetAdditionalStyleContext(int32_t aIndex) const
  346. {
  347. switch (aIndex) {
  348. case NS_SQR_CHAR_STYLE_CONTEXT_INDEX:
  349. return mSqrChar.GetStyleContext();
  350. default:
  351. return nullptr;
  352. }
  353. }
  354. void
  355. nsMathMLmrootFrame::SetAdditionalStyleContext(int32_t aIndex,
  356. nsStyleContext* aStyleContext)
  357. {
  358. switch (aIndex) {
  359. case NS_SQR_CHAR_STYLE_CONTEXT_INDEX:
  360. mSqrChar.SetStyleContext(aStyleContext);
  361. break;
  362. }
  363. }