nsMathMLmencloseFrame.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868
  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 "nsMathMLmencloseFrame.h"
  6. #include "gfx2DGlue.h"
  7. #include "gfxUtils.h"
  8. #include "mozilla/gfx/2D.h"
  9. #include "mozilla/gfx/PathHelpers.h"
  10. #include "nsPresContext.h"
  11. #include "nsRenderingContext.h"
  12. #include "nsWhitespaceTokenizer.h"
  13. #include "nsDisplayList.h"
  14. #include "gfxContext.h"
  15. #include "nsMathMLChar.h"
  16. #include <algorithm>
  17. using namespace mozilla;
  18. using namespace mozilla::gfx;
  19. //
  20. // <menclose> -- enclose content with a stretching symbol such
  21. // as a long division sign. - implementation
  22. // longdiv:
  23. // Unicode 5.1 assigns U+27CC to LONG DIVISION, but a right parenthesis
  24. // renders better with current font support.
  25. static const char16_t kLongDivChar = ')';
  26. // radical: 'SQUARE ROOT'
  27. static const char16_t kRadicalChar = 0x221A;
  28. // updiagonalstrike
  29. static const uint8_t kArrowHeadSize = 10;
  30. // phasorangle
  31. static const uint8_t kPhasorangleWidth = 8;
  32. nsIFrame*
  33. NS_NewMathMLmencloseFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
  34. {
  35. return new (aPresShell) nsMathMLmencloseFrame(aContext);
  36. }
  37. NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmencloseFrame)
  38. nsMathMLmencloseFrame::nsMathMLmencloseFrame(nsStyleContext* aContext) :
  39. nsMathMLContainerFrame(aContext), mNotationsToDraw(0),
  40. mLongDivCharIndex(-1), mRadicalCharIndex(-1), mContentWidth(0)
  41. {
  42. }
  43. nsMathMLmencloseFrame::~nsMathMLmencloseFrame()
  44. {
  45. }
  46. nsresult nsMathMLmencloseFrame::AllocateMathMLChar(nsMencloseNotation mask)
  47. {
  48. // Is the char already allocated?
  49. if ((mask == NOTATION_LONGDIV && mLongDivCharIndex >= 0) ||
  50. (mask == NOTATION_RADICAL && mRadicalCharIndex >= 0))
  51. return NS_OK;
  52. // No need to track the style context given to our MathML chars.
  53. // The Style System will use Get/SetAdditionalStyleContext() to keep it
  54. // up-to-date if dynamic changes arise.
  55. uint32_t i = mMathMLChar.Length();
  56. nsAutoString Char;
  57. if (!mMathMLChar.AppendElement())
  58. return NS_ERROR_OUT_OF_MEMORY;
  59. if (mask == NOTATION_LONGDIV) {
  60. Char.Assign(kLongDivChar);
  61. mLongDivCharIndex = i;
  62. } else if (mask == NOTATION_RADICAL) {
  63. Char.Assign(kRadicalChar);
  64. mRadicalCharIndex = i;
  65. }
  66. nsPresContext *presContext = PresContext();
  67. mMathMLChar[i].SetData(Char);
  68. ResolveMathMLCharStyle(presContext, mContent, mStyleContext, &mMathMLChar[i]);
  69. return NS_OK;
  70. }
  71. /*
  72. * Add a notation to draw, if the argument is the name of a known notation.
  73. * @param aNotation string name of a notation
  74. */
  75. nsresult nsMathMLmencloseFrame::AddNotation(const nsAString& aNotation)
  76. {
  77. nsresult rv;
  78. if (aNotation.EqualsLiteral("longdiv")) {
  79. rv = AllocateMathMLChar(NOTATION_LONGDIV);
  80. NS_ENSURE_SUCCESS(rv, rv);
  81. mNotationsToDraw |= NOTATION_LONGDIV;
  82. } else if (aNotation.EqualsLiteral("actuarial")) {
  83. mNotationsToDraw |= (NOTATION_RIGHT | NOTATION_TOP);
  84. } else if (aNotation.EqualsLiteral("radical")) {
  85. rv = AllocateMathMLChar(NOTATION_RADICAL);
  86. NS_ENSURE_SUCCESS(rv, rv);
  87. mNotationsToDraw |= NOTATION_RADICAL;
  88. } else if (aNotation.EqualsLiteral("box")) {
  89. mNotationsToDraw |= (NOTATION_LEFT | NOTATION_RIGHT |
  90. NOTATION_TOP | NOTATION_BOTTOM);
  91. } else if (aNotation.EqualsLiteral("roundedbox")) {
  92. mNotationsToDraw |= NOTATION_ROUNDEDBOX;
  93. } else if (aNotation.EqualsLiteral("circle")) {
  94. mNotationsToDraw |= NOTATION_CIRCLE;
  95. } else if (aNotation.EqualsLiteral("left")) {
  96. mNotationsToDraw |= NOTATION_LEFT;
  97. } else if (aNotation.EqualsLiteral("right")) {
  98. mNotationsToDraw |= NOTATION_RIGHT;
  99. } else if (aNotation.EqualsLiteral("top")) {
  100. mNotationsToDraw |= NOTATION_TOP;
  101. } else if (aNotation.EqualsLiteral("bottom")) {
  102. mNotationsToDraw |= NOTATION_BOTTOM;
  103. } else if (aNotation.EqualsLiteral("updiagonalstrike")) {
  104. mNotationsToDraw |= NOTATION_UPDIAGONALSTRIKE;
  105. } else if (aNotation.EqualsLiteral("updiagonalarrow")) {
  106. mNotationsToDraw |= NOTATION_UPDIAGONALARROW;
  107. } else if (aNotation.EqualsLiteral("downdiagonalstrike")) {
  108. mNotationsToDraw |= NOTATION_DOWNDIAGONALSTRIKE;
  109. } else if (aNotation.EqualsLiteral("verticalstrike")) {
  110. mNotationsToDraw |= NOTATION_VERTICALSTRIKE;
  111. } else if (aNotation.EqualsLiteral("horizontalstrike")) {
  112. mNotationsToDraw |= NOTATION_HORIZONTALSTRIKE;
  113. } else if (aNotation.EqualsLiteral("madruwb")) {
  114. mNotationsToDraw |= (NOTATION_RIGHT | NOTATION_BOTTOM);
  115. } else if (aNotation.EqualsLiteral("phasorangle")) {
  116. mNotationsToDraw |= (NOTATION_BOTTOM | NOTATION_PHASORANGLE);
  117. }
  118. return NS_OK;
  119. }
  120. /*
  121. * Initialize the list of notations to draw
  122. */
  123. void nsMathMLmencloseFrame::InitNotations()
  124. {
  125. mNotationsToDraw = 0;
  126. mLongDivCharIndex = mRadicalCharIndex = -1;
  127. mMathMLChar.Clear();
  128. nsAutoString value;
  129. if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::notation_, value)) {
  130. // parse the notation attribute
  131. nsWhitespaceTokenizer tokenizer(value);
  132. while (tokenizer.hasMoreTokens())
  133. AddNotation(tokenizer.nextToken());
  134. if (IsToDraw(NOTATION_UPDIAGONALARROW)) {
  135. // For <menclose notation="updiagonalstrike updiagonalarrow">, if
  136. // the two notations are drawn then the strike line may cause the point of
  137. // the arrow to be too wide. Hence we will only draw the updiagonalarrow
  138. // and the arrow shaft may be thought to be the updiagonalstrike.
  139. mNotationsToDraw &= ~NOTATION_UPDIAGONALSTRIKE;
  140. }
  141. } else {
  142. // default: longdiv
  143. if (NS_FAILED(AllocateMathMLChar(NOTATION_LONGDIV)))
  144. return;
  145. mNotationsToDraw = NOTATION_LONGDIV;
  146. }
  147. }
  148. NS_IMETHODIMP
  149. nsMathMLmencloseFrame::InheritAutomaticData(nsIFrame* aParent)
  150. {
  151. // let the base class get the default from our parent
  152. nsMathMLContainerFrame::InheritAutomaticData(aParent);
  153. mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY;
  154. InitNotations();
  155. return NS_OK;
  156. }
  157. NS_IMETHODIMP
  158. nsMathMLmencloseFrame::TransmitAutomaticData()
  159. {
  160. if (IsToDraw(NOTATION_RADICAL)) {
  161. // The TeXBook (Ch 17. p.141) says that \sqrt is cramped
  162. UpdatePresentationDataFromChildAt(0, -1,
  163. NS_MATHML_COMPRESSED,
  164. NS_MATHML_COMPRESSED);
  165. }
  166. return NS_OK;
  167. }
  168. void
  169. nsMathMLmencloseFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
  170. const nsDisplayListSet& aLists)
  171. {
  172. /////////////
  173. // paint the menclosed content
  174. nsMathMLContainerFrame::BuildDisplayList(aBuilder, aLists);
  175. if (NS_MATHML_HAS_ERROR(mPresentationData.flags))
  176. return;
  177. nsRect mencloseRect = nsIFrame::GetRect();
  178. mencloseRect.x = mencloseRect.y = 0;
  179. if (IsToDraw(NOTATION_RADICAL)) {
  180. mMathMLChar[mRadicalCharIndex].Display(aBuilder, this, aLists, 0);
  181. nsRect rect;
  182. mMathMLChar[mRadicalCharIndex].GetRect(rect);
  183. rect.MoveBy(StyleVisibility()->mDirection ? -mContentWidth : rect.width, 0);
  184. rect.SizeTo(mContentWidth, mRadicalRuleThickness);
  185. DisplayBar(aBuilder, this, rect, aLists);
  186. }
  187. if (IsToDraw(NOTATION_PHASORANGLE)) {
  188. DisplayNotation(aBuilder, this, mencloseRect, aLists,
  189. mRuleThickness, NOTATION_PHASORANGLE);
  190. }
  191. if (IsToDraw(NOTATION_LONGDIV)) {
  192. mMathMLChar[mLongDivCharIndex].Display(aBuilder, this, aLists, 1);
  193. nsRect rect;
  194. mMathMLChar[mLongDivCharIndex].GetRect(rect);
  195. rect.SizeTo(rect.width + mContentWidth, mRuleThickness);
  196. DisplayBar(aBuilder, this, rect, aLists);
  197. }
  198. if (IsToDraw(NOTATION_TOP)) {
  199. nsRect rect(0, 0, mencloseRect.width, mRuleThickness);
  200. DisplayBar(aBuilder, this, rect, aLists);
  201. }
  202. if (IsToDraw(NOTATION_BOTTOM)) {
  203. nsRect rect(0, mencloseRect.height - mRuleThickness,
  204. mencloseRect.width, mRuleThickness);
  205. DisplayBar(aBuilder, this, rect, aLists);
  206. }
  207. if (IsToDraw(NOTATION_LEFT)) {
  208. nsRect rect(0, 0, mRuleThickness, mencloseRect.height);
  209. DisplayBar(aBuilder, this, rect, aLists);
  210. }
  211. if (IsToDraw(NOTATION_RIGHT)) {
  212. nsRect rect(mencloseRect.width - mRuleThickness, 0,
  213. mRuleThickness, mencloseRect.height);
  214. DisplayBar(aBuilder, this, rect, aLists);
  215. }
  216. if (IsToDraw(NOTATION_ROUNDEDBOX)) {
  217. DisplayNotation(aBuilder, this, mencloseRect, aLists,
  218. mRuleThickness, NOTATION_ROUNDEDBOX);
  219. }
  220. if (IsToDraw(NOTATION_CIRCLE)) {
  221. DisplayNotation(aBuilder, this, mencloseRect, aLists,
  222. mRuleThickness, NOTATION_CIRCLE);
  223. }
  224. if (IsToDraw(NOTATION_UPDIAGONALSTRIKE)) {
  225. DisplayNotation(aBuilder, this, mencloseRect, aLists,
  226. mRuleThickness, NOTATION_UPDIAGONALSTRIKE);
  227. }
  228. if (IsToDraw(NOTATION_UPDIAGONALARROW)) {
  229. DisplayNotation(aBuilder, this, mencloseRect, aLists,
  230. mRuleThickness, NOTATION_UPDIAGONALARROW);
  231. }
  232. if (IsToDraw(NOTATION_DOWNDIAGONALSTRIKE)) {
  233. DisplayNotation(aBuilder, this, mencloseRect, aLists,
  234. mRuleThickness, NOTATION_DOWNDIAGONALSTRIKE);
  235. }
  236. if (IsToDraw(NOTATION_HORIZONTALSTRIKE)) {
  237. nsRect rect(0, mencloseRect.height / 2 - mRuleThickness / 2,
  238. mencloseRect.width, mRuleThickness);
  239. DisplayBar(aBuilder, this, rect, aLists);
  240. }
  241. if (IsToDraw(NOTATION_VERTICALSTRIKE)) {
  242. nsRect rect(mencloseRect.width / 2 - mRuleThickness / 2, 0,
  243. mRuleThickness, mencloseRect.height);
  244. DisplayBar(aBuilder, this, rect, aLists);
  245. }
  246. }
  247. /* virtual */ nsresult
  248. nsMathMLmencloseFrame::MeasureForWidth(DrawTarget* aDrawTarget,
  249. ReflowOutput& aDesiredSize)
  250. {
  251. return PlaceInternal(aDrawTarget, false, aDesiredSize, true);
  252. }
  253. /* virtual */ nsresult
  254. nsMathMLmencloseFrame::Place(DrawTarget* aDrawTarget,
  255. bool aPlaceOrigin,
  256. ReflowOutput& aDesiredSize)
  257. {
  258. return PlaceInternal(aDrawTarget, aPlaceOrigin, aDesiredSize, false);
  259. }
  260. /* virtual */ nsresult
  261. nsMathMLmencloseFrame::PlaceInternal(DrawTarget* aDrawTarget,
  262. bool aPlaceOrigin,
  263. ReflowOutput& aDesiredSize,
  264. bool aWidthOnly)
  265. {
  266. ///////////////
  267. // Measure the size of our content using the base class to format like an
  268. // inferred mrow.
  269. ReflowOutput baseSize(aDesiredSize.GetWritingMode());
  270. nsresult rv =
  271. nsMathMLContainerFrame::Place(aDrawTarget, false, baseSize);
  272. if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) {
  273. DidReflowChildren(PrincipalChildList().FirstChild());
  274. return rv;
  275. }
  276. nsBoundingMetrics bmBase = baseSize.mBoundingMetrics;
  277. nscoord dx_left = 0, dx_right = 0;
  278. nsBoundingMetrics bmLongdivChar, bmRadicalChar;
  279. nscoord radicalAscent = 0, radicalDescent = 0;
  280. nscoord longdivAscent = 0, longdivDescent = 0;
  281. nscoord psi = 0;
  282. nscoord leading = 0;
  283. ///////////////
  284. // Thickness of bars and font metrics
  285. nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
  286. float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
  287. RefPtr<nsFontMetrics> fm =
  288. nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);
  289. GetRuleThickness(aDrawTarget, fm, mRuleThickness);
  290. if (mRuleThickness < onePixel) {
  291. mRuleThickness = onePixel;
  292. }
  293. char16_t one = '1';
  294. nsBoundingMetrics bmOne =
  295. nsLayoutUtils::AppUnitBoundsOfString(&one, 1, *fm, aDrawTarget);
  296. ///////////////
  297. // General rules: the menclose element takes the size of the enclosed content.
  298. // We add a padding when needed.
  299. // determine padding & psi
  300. nscoord padding = 3 * mRuleThickness;
  301. nscoord delta = padding % onePixel;
  302. if (delta)
  303. padding += onePixel - delta; // round up
  304. if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) {
  305. GetRadicalParameters(fm, StyleFont()->mMathDisplay ==
  306. NS_MATHML_DISPLAYSTYLE_BLOCK,
  307. mRadicalRuleThickness, leading, psi);
  308. // make sure that the rule appears on on screen
  309. if (mRadicalRuleThickness < onePixel) {
  310. mRadicalRuleThickness = onePixel;
  311. }
  312. // adjust clearance psi to get an exact number of pixels -- this
  313. // gives a nicer & uniform look on stacked radicals (bug 130282)
  314. delta = psi % onePixel;
  315. if (delta) {
  316. psi += onePixel - delta; // round up
  317. }
  318. }
  319. // Set horizontal parameters
  320. if (IsToDraw(NOTATION_ROUNDEDBOX) ||
  321. IsToDraw(NOTATION_TOP) ||
  322. IsToDraw(NOTATION_LEFT) ||
  323. IsToDraw(NOTATION_BOTTOM) ||
  324. IsToDraw(NOTATION_CIRCLE))
  325. dx_left = padding;
  326. if (IsToDraw(NOTATION_ROUNDEDBOX) ||
  327. IsToDraw(NOTATION_TOP) ||
  328. IsToDraw(NOTATION_RIGHT) ||
  329. IsToDraw(NOTATION_BOTTOM) ||
  330. IsToDraw(NOTATION_CIRCLE))
  331. dx_right = padding;
  332. // Set vertical parameters
  333. if (IsToDraw(NOTATION_RIGHT) ||
  334. IsToDraw(NOTATION_LEFT) ||
  335. IsToDraw(NOTATION_UPDIAGONALSTRIKE) ||
  336. IsToDraw(NOTATION_UPDIAGONALARROW) ||
  337. IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) ||
  338. IsToDraw(NOTATION_VERTICALSTRIKE) ||
  339. IsToDraw(NOTATION_CIRCLE) ||
  340. IsToDraw(NOTATION_ROUNDEDBOX) ||
  341. IsToDraw(NOTATION_RADICAL) ||
  342. IsToDraw(NOTATION_LONGDIV) ||
  343. IsToDraw(NOTATION_PHASORANGLE)) {
  344. // set a minimal value for the base height
  345. bmBase.ascent = std::max(bmOne.ascent, bmBase.ascent);
  346. bmBase.descent = std::max(0, bmBase.descent);
  347. }
  348. mBoundingMetrics.ascent = bmBase.ascent;
  349. mBoundingMetrics.descent = bmBase.descent;
  350. if (IsToDraw(NOTATION_ROUNDEDBOX) ||
  351. IsToDraw(NOTATION_TOP) ||
  352. IsToDraw(NOTATION_LEFT) ||
  353. IsToDraw(NOTATION_RIGHT) ||
  354. IsToDraw(NOTATION_CIRCLE))
  355. mBoundingMetrics.ascent += padding;
  356. if (IsToDraw(NOTATION_ROUNDEDBOX) ||
  357. IsToDraw(NOTATION_LEFT) ||
  358. IsToDraw(NOTATION_RIGHT) ||
  359. IsToDraw(NOTATION_BOTTOM) ||
  360. IsToDraw(NOTATION_CIRCLE))
  361. mBoundingMetrics.descent += padding;
  362. ///////////////
  363. // phasorangle notation
  364. if (IsToDraw(NOTATION_PHASORANGLE)) {
  365. nscoord phasorangleWidth = kPhasorangleWidth * mRuleThickness;
  366. // Update horizontal parameters
  367. dx_left = std::max(dx_left, phasorangleWidth);
  368. }
  369. ///////////////
  370. // updiagonal arrow notation. We need enough space at the top right corner to
  371. // draw the arrow head.
  372. if (IsToDraw(NOTATION_UPDIAGONALARROW)) {
  373. // This is an estimate, see nsDisplayNotation::Paint for the exact head size
  374. nscoord arrowHeadSize = kArrowHeadSize * mRuleThickness;
  375. // We want that the arrow shaft strikes the menclose content and that the
  376. // arrow head does not overlap with that content. Hence we add some space
  377. // on the right. We don't add space on the top but only ensure that the
  378. // ascent is large enough.
  379. dx_right = std::max(dx_right, arrowHeadSize);
  380. mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, arrowHeadSize);
  381. }
  382. ///////////////
  383. // circle notation: we don't want the ellipse to overlap the enclosed
  384. // content. Hence, we need to increase the size of the bounding box by a
  385. // factor of at least sqrt(2).
  386. if (IsToDraw(NOTATION_CIRCLE)) {
  387. double ratio = (sqrt(2.0) - 1.0) / 2.0;
  388. nscoord padding2;
  389. // Update horizontal parameters
  390. padding2 = ratio * bmBase.width;
  391. dx_left = std::max(dx_left, padding2);
  392. dx_right = std::max(dx_right, padding2);
  393. // Update vertical parameters
  394. padding2 = ratio * (bmBase.ascent + bmBase.descent);
  395. mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent,
  396. bmBase.ascent + padding2);
  397. mBoundingMetrics.descent = std::max(mBoundingMetrics.descent,
  398. bmBase.descent + padding2);
  399. }
  400. ///////////////
  401. // longdiv notation:
  402. if (IsToDraw(NOTATION_LONGDIV)) {
  403. if (aWidthOnly) {
  404. nscoord longdiv_width = mMathMLChar[mLongDivCharIndex].
  405. GetMaxWidth(PresContext(), aDrawTarget, fontSizeInflation);
  406. // Update horizontal parameters
  407. dx_left = std::max(dx_left, longdiv_width);
  408. } else {
  409. // Stretch the parenthesis to the appropriate height if it is not
  410. // big enough.
  411. nsBoundingMetrics contSize = bmBase;
  412. contSize.ascent = mRuleThickness;
  413. contSize.descent = bmBase.ascent + bmBase.descent + psi;
  414. // height(longdiv) should be >= height(base) + psi + mRuleThickness
  415. mMathMLChar[mLongDivCharIndex].Stretch(PresContext(), aDrawTarget,
  416. fontSizeInflation,
  417. NS_STRETCH_DIRECTION_VERTICAL,
  418. contSize, bmLongdivChar,
  419. NS_STRETCH_LARGER, false);
  420. mMathMLChar[mLongDivCharIndex].GetBoundingMetrics(bmLongdivChar);
  421. // Update horizontal parameters
  422. dx_left = std::max(dx_left, bmLongdivChar.width);
  423. // Update vertical parameters
  424. longdivAscent = bmBase.ascent + psi + mRuleThickness;
  425. longdivDescent = std::max(bmBase.descent,
  426. (bmLongdivChar.ascent + bmLongdivChar.descent -
  427. longdivAscent));
  428. mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent,
  429. longdivAscent);
  430. mBoundingMetrics.descent = std::max(mBoundingMetrics.descent,
  431. longdivDescent);
  432. }
  433. }
  434. ///////////////
  435. // radical notation:
  436. if (IsToDraw(NOTATION_RADICAL)) {
  437. nscoord *dx_leading = StyleVisibility()->mDirection ? &dx_right : &dx_left;
  438. if (aWidthOnly) {
  439. nscoord radical_width = mMathMLChar[mRadicalCharIndex].
  440. GetMaxWidth(PresContext(), aDrawTarget, fontSizeInflation);
  441. // Update horizontal parameters
  442. *dx_leading = std::max(*dx_leading, radical_width);
  443. } else {
  444. // Stretch the radical symbol to the appropriate height if it is not
  445. // big enough.
  446. nsBoundingMetrics contSize = bmBase;
  447. contSize.ascent = mRadicalRuleThickness;
  448. contSize.descent = bmBase.ascent + bmBase.descent + psi;
  449. // height(radical) should be >= height(base) + psi + mRadicalRuleThickness
  450. mMathMLChar[mRadicalCharIndex].Stretch(PresContext(), aDrawTarget,
  451. fontSizeInflation,
  452. NS_STRETCH_DIRECTION_VERTICAL,
  453. contSize, bmRadicalChar,
  454. NS_STRETCH_LARGER,
  455. StyleVisibility()->mDirection);
  456. mMathMLChar[mRadicalCharIndex].GetBoundingMetrics(bmRadicalChar);
  457. // Update horizontal parameters
  458. *dx_leading = std::max(*dx_leading, bmRadicalChar.width);
  459. // Update vertical parameters
  460. radicalAscent = bmBase.ascent + psi + mRadicalRuleThickness;
  461. radicalDescent = std::max(bmBase.descent,
  462. (bmRadicalChar.ascent + bmRadicalChar.descent -
  463. radicalAscent));
  464. mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent,
  465. radicalAscent);
  466. mBoundingMetrics.descent = std::max(mBoundingMetrics.descent,
  467. radicalDescent);
  468. }
  469. }
  470. ///////////////
  471. //
  472. if (IsToDraw(NOTATION_CIRCLE) ||
  473. IsToDraw(NOTATION_ROUNDEDBOX) ||
  474. (IsToDraw(NOTATION_LEFT) && IsToDraw(NOTATION_RIGHT))) {
  475. // center the menclose around the content (horizontally)
  476. dx_left = dx_right = std::max(dx_left, dx_right);
  477. }
  478. ///////////////
  479. // The maximum size is now computed: set the remaining parameters
  480. mBoundingMetrics.width = dx_left + bmBase.width + dx_right;
  481. mBoundingMetrics.leftBearing = std::min(0, dx_left + bmBase.leftBearing);
  482. mBoundingMetrics.rightBearing =
  483. std::max(mBoundingMetrics.width, dx_left + bmBase.rightBearing);
  484. aDesiredSize.Width() = mBoundingMetrics.width;
  485. aDesiredSize.SetBlockStartAscent(std::max(mBoundingMetrics.ascent,
  486. baseSize.BlockStartAscent()));
  487. aDesiredSize.Height() = aDesiredSize.BlockStartAscent() +
  488. std::max(mBoundingMetrics.descent,
  489. baseSize.Height() - baseSize.BlockStartAscent());
  490. if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) {
  491. nscoord desiredSizeAscent = aDesiredSize.BlockStartAscent();
  492. nscoord desiredSizeDescent = aDesiredSize.Height() -
  493. aDesiredSize.BlockStartAscent();
  494. if (IsToDraw(NOTATION_LONGDIV)) {
  495. desiredSizeAscent = std::max(desiredSizeAscent,
  496. longdivAscent + leading);
  497. desiredSizeDescent = std::max(desiredSizeDescent,
  498. longdivDescent + mRuleThickness);
  499. }
  500. if (IsToDraw(NOTATION_RADICAL)) {
  501. desiredSizeAscent = std::max(desiredSizeAscent,
  502. radicalAscent + leading);
  503. desiredSizeDescent = std::max(desiredSizeDescent,
  504. radicalDescent + mRadicalRuleThickness);
  505. }
  506. aDesiredSize.SetBlockStartAscent(desiredSizeAscent);
  507. aDesiredSize.Height() = desiredSizeAscent + desiredSizeDescent;
  508. }
  509. if (IsToDraw(NOTATION_CIRCLE) ||
  510. IsToDraw(NOTATION_ROUNDEDBOX) ||
  511. (IsToDraw(NOTATION_TOP) && IsToDraw(NOTATION_BOTTOM))) {
  512. // center the menclose around the content (vertically)
  513. nscoord dy = std::max(aDesiredSize.BlockStartAscent() - bmBase.ascent,
  514. aDesiredSize.Height() -
  515. aDesiredSize.BlockStartAscent() - bmBase.descent);
  516. aDesiredSize.SetBlockStartAscent(bmBase.ascent + dy);
  517. aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + bmBase.descent + dy;
  518. }
  519. // Update mBoundingMetrics ascent/descent
  520. if (IsToDraw(NOTATION_TOP) ||
  521. IsToDraw(NOTATION_RIGHT) ||
  522. IsToDraw(NOTATION_LEFT) ||
  523. IsToDraw(NOTATION_UPDIAGONALSTRIKE) ||
  524. IsToDraw(NOTATION_UPDIAGONALARROW) ||
  525. IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) ||
  526. IsToDraw(NOTATION_VERTICALSTRIKE) ||
  527. IsToDraw(NOTATION_CIRCLE) ||
  528. IsToDraw(NOTATION_ROUNDEDBOX))
  529. mBoundingMetrics.ascent = aDesiredSize.BlockStartAscent();
  530. if (IsToDraw(NOTATION_BOTTOM) ||
  531. IsToDraw(NOTATION_RIGHT) ||
  532. IsToDraw(NOTATION_LEFT) ||
  533. IsToDraw(NOTATION_UPDIAGONALSTRIKE) ||
  534. IsToDraw(NOTATION_UPDIAGONALARROW) ||
  535. IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) ||
  536. IsToDraw(NOTATION_VERTICALSTRIKE) ||
  537. IsToDraw(NOTATION_CIRCLE) ||
  538. IsToDraw(NOTATION_ROUNDEDBOX))
  539. mBoundingMetrics.descent = aDesiredSize.Height() - aDesiredSize.BlockStartAscent();
  540. // phasorangle notation:
  541. // move up from the bottom by the angled line height
  542. if (IsToDraw(NOTATION_PHASORANGLE))
  543. mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, 2 * kPhasorangleWidth * mRuleThickness - mBoundingMetrics.descent);
  544. aDesiredSize.mBoundingMetrics = mBoundingMetrics;
  545. mReference.x = 0;
  546. mReference.y = aDesiredSize.BlockStartAscent();
  547. if (aPlaceOrigin) {
  548. //////////////////
  549. // Set position and size of MathMLChars
  550. if (IsToDraw(NOTATION_LONGDIV))
  551. mMathMLChar[mLongDivCharIndex].SetRect(nsRect(dx_left -
  552. bmLongdivChar.width,
  553. aDesiredSize.BlockStartAscent() -
  554. longdivAscent,
  555. bmLongdivChar.width,
  556. bmLongdivChar.ascent +
  557. bmLongdivChar.descent));
  558. if (IsToDraw(NOTATION_RADICAL)) {
  559. nscoord dx = (StyleVisibility()->mDirection ?
  560. dx_left + bmBase.width : dx_left - bmRadicalChar.width);
  561. mMathMLChar[mRadicalCharIndex].SetRect(nsRect(dx,
  562. aDesiredSize.BlockStartAscent() -
  563. radicalAscent,
  564. bmRadicalChar.width,
  565. bmRadicalChar.ascent +
  566. bmRadicalChar.descent));
  567. }
  568. mContentWidth = bmBase.width;
  569. //////////////////
  570. // Finish reflowing child frames
  571. PositionRowChildFrames(dx_left, aDesiredSize.BlockStartAscent());
  572. }
  573. return NS_OK;
  574. }
  575. nscoord
  576. nsMathMLmencloseFrame::FixInterFrameSpacing(ReflowOutput& aDesiredSize)
  577. {
  578. nscoord gap = nsMathMLContainerFrame::FixInterFrameSpacing(aDesiredSize);
  579. if (!gap)
  580. return 0;
  581. // Move the MathML characters
  582. nsRect rect;
  583. for (uint32_t i = 0; i < mMathMLChar.Length(); i++) {
  584. mMathMLChar[i].GetRect(rect);
  585. rect.MoveBy(gap, 0);
  586. mMathMLChar[i].SetRect(rect);
  587. }
  588. return gap;
  589. }
  590. nsresult
  591. nsMathMLmencloseFrame::AttributeChanged(int32_t aNameSpaceID,
  592. nsIAtom* aAttribute,
  593. int32_t aModType)
  594. {
  595. if (aAttribute == nsGkAtoms::notation_) {
  596. InitNotations();
  597. }
  598. return nsMathMLContainerFrame::
  599. AttributeChanged(aNameSpaceID, aAttribute, aModType);
  600. }
  601. //////////////////
  602. // the Style System will use these to pass the proper style context to our
  603. // MathMLChar
  604. nsStyleContext*
  605. nsMathMLmencloseFrame::GetAdditionalStyleContext(int32_t aIndex) const
  606. {
  607. int32_t len = mMathMLChar.Length();
  608. if (aIndex >= 0 && aIndex < len)
  609. return mMathMLChar[aIndex].GetStyleContext();
  610. else
  611. return nullptr;
  612. }
  613. void
  614. nsMathMLmencloseFrame::SetAdditionalStyleContext(int32_t aIndex,
  615. nsStyleContext* aStyleContext)
  616. {
  617. int32_t len = mMathMLChar.Length();
  618. if (aIndex >= 0 && aIndex < len)
  619. mMathMLChar[aIndex].SetStyleContext(aStyleContext);
  620. }
  621. class nsDisplayNotation : public nsDisplayItem
  622. {
  623. public:
  624. nsDisplayNotation(nsDisplayListBuilder* aBuilder,
  625. nsIFrame* aFrame, const nsRect& aRect,
  626. nscoord aThickness, nsMencloseNotation aType)
  627. : nsDisplayItem(aBuilder, aFrame), mRect(aRect),
  628. mThickness(aThickness), mType(aType) {
  629. MOZ_COUNT_CTOR(nsDisplayNotation);
  630. }
  631. #ifdef NS_BUILD_REFCNT_LOGGING
  632. virtual ~nsDisplayNotation() {
  633. MOZ_COUNT_DTOR(nsDisplayNotation);
  634. }
  635. #endif
  636. virtual void Paint(nsDisplayListBuilder* aBuilder,
  637. nsRenderingContext* aCtx) override;
  638. NS_DISPLAY_DECL_NAME("MathMLMencloseNotation", TYPE_MATHML_MENCLOSE_NOTATION)
  639. private:
  640. nsRect mRect;
  641. nscoord mThickness;
  642. nsMencloseNotation mType;
  643. };
  644. void nsDisplayNotation::Paint(nsDisplayListBuilder* aBuilder,
  645. nsRenderingContext* aCtx)
  646. {
  647. DrawTarget& aDrawTarget = *aCtx->GetDrawTarget();
  648. nsPresContext* presContext = mFrame->PresContext();
  649. Float strokeWidth = presContext->AppUnitsToGfxUnits(mThickness);
  650. Rect rect = NSRectToRect(mRect + ToReferenceFrame(),
  651. presContext->AppUnitsPerDevPixel());
  652. rect.Deflate(strokeWidth / 2.f);
  653. ColorPattern color(ToDeviceColor(
  654. mFrame->GetVisitedDependentColor(eCSSProperty__webkit_text_fill_color)));
  655. StrokeOptions strokeOptions(strokeWidth);
  656. switch(mType)
  657. {
  658. case NOTATION_CIRCLE: {
  659. RefPtr<Path> ellipse =
  660. MakePathForEllipse(aDrawTarget, rect.Center(), rect.Size());
  661. aDrawTarget.Stroke(ellipse, color, strokeOptions);
  662. return;
  663. }
  664. case NOTATION_ROUNDEDBOX: {
  665. Float radius = 3 * strokeWidth;
  666. RectCornerRadii radii(radius, radius);
  667. RefPtr<Path> roundedRect =
  668. MakePathForRoundedRect(aDrawTarget, rect, radii, true);
  669. aDrawTarget.Stroke(roundedRect, color, strokeOptions);
  670. return;
  671. }
  672. case NOTATION_UPDIAGONALSTRIKE: {
  673. aDrawTarget.StrokeLine(rect.BottomLeft(), rect.TopRight(),
  674. color, strokeOptions);
  675. return;
  676. }
  677. case NOTATION_DOWNDIAGONALSTRIKE: {
  678. aDrawTarget.StrokeLine(rect.TopLeft(), rect.BottomRight(),
  679. color, strokeOptions);
  680. return;
  681. }
  682. case NOTATION_UPDIAGONALARROW: {
  683. // Compute some parameters to draw the updiagonalarrow. The values below
  684. // are taken from MathJax's HTML-CSS output.
  685. Float W = rect.Width(); gfxFloat H = rect.Height();
  686. Float l = sqrt(W*W + H*H);
  687. Float f = Float(kArrowHeadSize) * strokeWidth / l;
  688. Float w = W * f; gfxFloat h = H * f;
  689. // Draw the arrow shaft
  690. aDrawTarget.StrokeLine(rect.BottomLeft(),
  691. rect.TopRight() + Point(-.7*w, .7*h),
  692. color, strokeOptions);
  693. // Draw the arrow head
  694. RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
  695. builder->MoveTo(rect.TopRight());
  696. builder->LineTo(rect.TopRight() + Point(-w -.4*h, std::max(-strokeWidth / 2.0, h - .4*w)));
  697. builder->LineTo(rect.TopRight() + Point(-.7*w, .7*h));
  698. builder->LineTo(rect.TopRight() + Point(std::min(strokeWidth / 2.0, -w + .4*h), h + .4*w));
  699. builder->Close();
  700. RefPtr<Path> path = builder->Finish();
  701. aDrawTarget.Fill(path, color);
  702. return;
  703. }
  704. case NOTATION_PHASORANGLE: {
  705. // Compute some parameters to draw the angled line,
  706. // that uses a slope of 2 (angle = tan^-1(2)).
  707. // H = w * tan(angle) = w * 2
  708. Float w = Float(kPhasorangleWidth) * strokeWidth;
  709. Float H = 2 * w;
  710. // Draw the angled line
  711. aDrawTarget.StrokeLine(rect.BottomLeft(),
  712. rect.BottomLeft() + Point(w, -H),
  713. color, strokeOptions);
  714. return;
  715. }
  716. default:
  717. NS_NOTREACHED("This notation can not be drawn using nsDisplayNotation");
  718. }
  719. }
  720. void
  721. nsMathMLmencloseFrame::DisplayNotation(nsDisplayListBuilder* aBuilder,
  722. nsIFrame* aFrame, const nsRect& aRect,
  723. const nsDisplayListSet& aLists,
  724. nscoord aThickness,
  725. nsMencloseNotation aType)
  726. {
  727. if (!aFrame->StyleVisibility()->IsVisible() || aRect.IsEmpty() ||
  728. aThickness <= 0)
  729. return;
  730. aLists.Content()->AppendNewToTop(new (aBuilder)
  731. nsDisplayNotation(aBuilder, aFrame, aRect, aThickness, aType));
  732. }