nsMathMLChar.cpp 89 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487
  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 "nsMathMLChar.h"
  6. #include "gfxTextRun.h"
  7. #include "gfxUtils.h"
  8. #include "mozilla/gfx/2D.h"
  9. #include "mozilla/MathAlgorithms.h"
  10. #include "mozilla/Unused.h"
  11. #include "nsAutoPtr.h"
  12. #include "nsCOMPtr.h"
  13. #include "nsDeviceContext.h"
  14. #include "nsFontMetrics.h"
  15. #include "nsIFrame.h"
  16. #include "nsLayoutUtils.h"
  17. #include "nsPresContext.h"
  18. #include "nsStyleContext.h"
  19. #include "nsUnicharUtils.h"
  20. #include "nsRenderingContext.h"
  21. #include "mozilla/Preferences.h"
  22. #include "nsIPersistentProperties2.h"
  23. #include "nsIObserverService.h"
  24. #include "nsIObserver.h"
  25. #include "nsNetUtil.h"
  26. #include "nsContentUtils.h"
  27. #include "mozilla/LookAndFeel.h"
  28. #include "nsCSSRendering.h"
  29. #include "mozilla/Sprintf.h"
  30. #include "nsDisplayList.h"
  31. #include "nsMathMLOperators.h"
  32. #include <algorithm>
  33. #include "gfxMathTable.h"
  34. #include "nsUnicodeScriptCodes.h"
  35. using namespace mozilla;
  36. using namespace mozilla::gfx;
  37. using namespace mozilla::image;
  38. //#define NOISY_SEARCH 1
  39. // BUG 848725 Drawing failure with stretchy horizontal parenthesis when no fonts
  40. // are installed. "kMaxScaleFactor" is required to limit the scale for the
  41. // vertical and horizontal stretchy operators.
  42. static const float kMaxScaleFactor = 20.0;
  43. static const float kLargeOpFactor = float(M_SQRT2);
  44. static const float kIntegralFactor = 2.0;
  45. static void
  46. NormalizeDefaultFont(nsFont& aFont, float aFontSizeInflation)
  47. {
  48. if (aFont.fontlist.GetDefaultFontType() != eFamily_none) {
  49. aFont.fontlist.Append(FontFamilyName(aFont.fontlist.GetDefaultFontType()));
  50. aFont.fontlist.SetDefaultFontType(eFamily_none);
  51. }
  52. aFont.size = NSToCoordRound(aFont.size * aFontSizeInflation);
  53. }
  54. // -----------------------------------------------------------------------------
  55. static const nsGlyphCode kNullGlyph = {{{0, 0}}, 0};
  56. // -----------------------------------------------------------------------------
  57. // nsGlyphTable is a class that provides an interface for accessing glyphs
  58. // of stretchy chars. It acts like a table that stores the variants of bigger
  59. // sizes (if any) and the partial glyphs needed to build extensible symbols.
  60. //
  61. // Bigger sizes (if any) of the char can then be retrieved with BigOf(...).
  62. // Partial glyphs can be retrieved with ElementAt(...).
  63. //
  64. // A table consists of "nsGlyphCode"s which are viewed either as Unicode
  65. // points (for nsPropertiesTable) or as direct glyph indices (for
  66. // nsOpenTypeTable)
  67. // -----------------------------------------------------------------------------
  68. class nsGlyphTable {
  69. public:
  70. virtual ~nsGlyphTable() {}
  71. virtual const FontFamilyName&
  72. FontNameFor(const nsGlyphCode& aGlyphCode) const = 0;
  73. // Getters for the parts
  74. virtual nsGlyphCode ElementAt(DrawTarget* aDrawTarget,
  75. int32_t aAppUnitsPerDevPixel,
  76. gfxFontGroup* aFontGroup,
  77. char16_t aChar,
  78. bool aVertical,
  79. uint32_t aPosition) = 0;
  80. virtual nsGlyphCode BigOf(DrawTarget* aDrawTarget,
  81. int32_t aAppUnitsPerDevPixel,
  82. gfxFontGroup* aFontGroup,
  83. char16_t aChar,
  84. bool aVertical,
  85. uint32_t aSize) = 0;
  86. // True if this table contains parts to render this char
  87. virtual bool HasPartsOf(DrawTarget* aDrawTarget,
  88. int32_t aAppUnitsPerDevPixel,
  89. gfxFontGroup* aFontGroup,
  90. char16_t aChar,
  91. bool aVertical) = 0;
  92. virtual already_AddRefed<gfxTextRun>
  93. MakeTextRun(DrawTarget* aDrawTarget,
  94. int32_t aAppUnitsPerDevPixel,
  95. gfxFontGroup* aFontGroup,
  96. const nsGlyphCode& aGlyph) = 0;
  97. protected:
  98. nsGlyphTable() : mCharCache(0) {}
  99. // For speedy re-use, we always cache the last data used in the table.
  100. // mCharCache is the Unicode point of the last char that was queried in this
  101. // table.
  102. char16_t mCharCache;
  103. };
  104. // An instance of nsPropertiesTable is associated with one primary font. Extra
  105. // glyphs can be taken in other additional fonts when stretching certain
  106. // characters.
  107. // These supplementary fonts are referred to as "external" fonts to the table.
  108. // General format of MathFont Property Files from which glyph data are
  109. // retrieved:
  110. // -----------------------------------------------------------------------------
  111. // Each font should have its set of glyph data. For example, the glyph data for
  112. // the "Symbol" font and the "MT Extra" font are in "mathfontSymbol.properties"
  113. // and "mathfontMTExtra.properties", respectively. The mathfont property file
  114. // is a set of all the stretchy MathML characters that can be rendered with that
  115. // font using larger and/or partial glyphs. The entry of each stretchy character
  116. // in the mathfont property file gives, in that order, the 4 partial glyphs:
  117. // Top (or Left), Middle, Bottom (or Right), Glue; and the variants of bigger
  118. // sizes (if any).
  119. // A position that is not relevant to a particular character is indicated there
  120. // with the UNICODE REPLACEMENT CHARACTER 0xFFFD.
  121. // -----------------------------------------------------------------------------
  122. #define NS_TABLE_STATE_ERROR -1
  123. #define NS_TABLE_STATE_EMPTY 0
  124. #define NS_TABLE_STATE_READY 1
  125. // helper to trim off comments from data in a MathFont Property File
  126. static void
  127. Clean(nsString& aValue)
  128. {
  129. // chop the trailing # comment portion if any ...
  130. int32_t comment = aValue.RFindChar('#');
  131. if (comment > 0) aValue.Truncate(comment);
  132. aValue.CompressWhitespace();
  133. }
  134. // helper to load a MathFont Property File
  135. static nsresult
  136. LoadProperties(const nsString& aName,
  137. nsCOMPtr<nsIPersistentProperties>& aProperties)
  138. {
  139. nsAutoString uriStr;
  140. uriStr.AssignLiteral("resource://gre/res/fonts/mathfont");
  141. uriStr.Append(aName);
  142. uriStr.StripWhitespace(); // that may come from aName
  143. uriStr.AppendLiteral(".properties");
  144. return NS_LoadPersistentPropertiesFromURISpec(getter_AddRefs(aProperties),
  145. NS_ConvertUTF16toUTF8(uriStr));
  146. }
  147. class nsPropertiesTable final : public nsGlyphTable {
  148. public:
  149. explicit nsPropertiesTable(const nsString& aPrimaryFontName)
  150. : mState(NS_TABLE_STATE_EMPTY)
  151. {
  152. MOZ_COUNT_CTOR(nsPropertiesTable);
  153. mGlyphCodeFonts.AppendElement(FontFamilyName(aPrimaryFontName, eUnquotedName));
  154. }
  155. ~nsPropertiesTable()
  156. {
  157. MOZ_COUNT_DTOR(nsPropertiesTable);
  158. }
  159. const FontFamilyName& PrimaryFontName() const
  160. {
  161. return mGlyphCodeFonts[0];
  162. }
  163. const FontFamilyName&
  164. FontNameFor(const nsGlyphCode& aGlyphCode) const override
  165. {
  166. NS_ASSERTION(!aGlyphCode.IsGlyphID(),
  167. "nsPropertiesTable can only access glyphs by code point");
  168. return mGlyphCodeFonts[aGlyphCode.font];
  169. }
  170. virtual nsGlyphCode ElementAt(DrawTarget* aDrawTarget,
  171. int32_t aAppUnitsPerDevPixel,
  172. gfxFontGroup* aFontGroup,
  173. char16_t aChar,
  174. bool aVertical,
  175. uint32_t aPosition) override;
  176. virtual nsGlyphCode BigOf(DrawTarget* aDrawTarget,
  177. int32_t aAppUnitsPerDevPixel,
  178. gfxFontGroup* aFontGroup,
  179. char16_t aChar,
  180. bool aVertical,
  181. uint32_t aSize) override
  182. {
  183. return ElementAt(aDrawTarget, aAppUnitsPerDevPixel, aFontGroup,
  184. aChar, aVertical, 4 + aSize);
  185. }
  186. virtual bool HasPartsOf(DrawTarget* aDrawTarget,
  187. int32_t aAppUnitsPerDevPixel,
  188. gfxFontGroup* aFontGroup,
  189. char16_t aChar,
  190. bool aVertical) override
  191. {
  192. return (ElementAt(aDrawTarget, aAppUnitsPerDevPixel, aFontGroup,
  193. aChar, aVertical, 0).Exists() ||
  194. ElementAt(aDrawTarget, aAppUnitsPerDevPixel, aFontGroup,
  195. aChar, aVertical, 1).Exists() ||
  196. ElementAt(aDrawTarget, aAppUnitsPerDevPixel, aFontGroup,
  197. aChar, aVertical, 2).Exists() ||
  198. ElementAt(aDrawTarget, aAppUnitsPerDevPixel, aFontGroup,
  199. aChar, aVertical, 3).Exists());
  200. }
  201. virtual already_AddRefed<gfxTextRun>
  202. MakeTextRun(DrawTarget* aDrawTarget,
  203. int32_t aAppUnitsPerDevPixel,
  204. gfxFontGroup* aFontGroup,
  205. const nsGlyphCode& aGlyph) override;
  206. private:
  207. // mGlyphCodeFonts[0] is the primary font associated to this table. The
  208. // others are possible "external" fonts for glyphs not in the primary font
  209. // but which are needed to stretch certain characters in the table
  210. nsTArray<FontFamilyName> mGlyphCodeFonts;
  211. // Tri-state variable for error/empty/ready
  212. int32_t mState;
  213. // The set of glyph data in this table, as provided by the MathFont Property
  214. // File
  215. nsCOMPtr<nsIPersistentProperties> mGlyphProperties;
  216. // mGlyphCache is a buffer containing the glyph data associated with
  217. // mCharCache.
  218. // For a property line 'key = value' in the MathFont Property File,
  219. // mCharCache will retain the 'key' -- which is a Unicode point, while
  220. // mGlyphCache will retain the 'value', which is a consecutive list of
  221. // nsGlyphCodes, i.e., the pairs of 'code@font' needed by the char -- in
  222. // which 'code@0' can be specified
  223. // without the optional '@0'. However, to ease subsequent processing,
  224. // mGlyphCache excludes the '@' symbol and explicitly inserts all optional '0'
  225. // that indicates the primary font identifier. Specifically therefore, the
  226. // k-th glyph is characterized by :
  227. // 1) mGlyphCache[3*k],mGlyphCache[3*k+1] : its Unicode point
  228. // 2) mGlyphCache[3*k+2] : the numeric identifier of the font where it comes
  229. // from.
  230. // A font identifier of '0' means the default primary font associated to this
  231. // table. Other digits map to the "external" fonts that may have been
  232. // specified in the MathFont Property File.
  233. nsString mGlyphCache;
  234. };
  235. /* virtual */
  236. nsGlyphCode
  237. nsPropertiesTable::ElementAt(DrawTarget* /* aDrawTarget */,
  238. int32_t /* aAppUnitsPerDevPixel */,
  239. gfxFontGroup* /* aFontGroup */,
  240. char16_t aChar,
  241. bool /* aVertical */,
  242. uint32_t aPosition)
  243. {
  244. if (mState == NS_TABLE_STATE_ERROR) return kNullGlyph;
  245. // Load glyph properties if this is the first time we have been here
  246. if (mState == NS_TABLE_STATE_EMPTY) {
  247. nsAutoString primaryFontName;
  248. mGlyphCodeFonts[0].AppendToString(primaryFontName);
  249. nsresult rv = LoadProperties(primaryFontName, mGlyphProperties);
  250. #ifdef DEBUG
  251. nsAutoCString uriStr;
  252. uriStr.AssignLiteral("resource://gre/res/fonts/mathfont");
  253. LossyAppendUTF16toASCII(primaryFontName, uriStr);
  254. uriStr.StripWhitespace(); // that may come from mGlyphCodeFonts
  255. uriStr.AppendLiteral(".properties");
  256. printf("Loading %s ... %s\n",
  257. uriStr.get(),
  258. (NS_FAILED(rv)) ? "Failed" : "Done");
  259. #endif
  260. if (NS_FAILED(rv)) {
  261. mState = NS_TABLE_STATE_ERROR; // never waste time with this table again
  262. return kNullGlyph;
  263. }
  264. mState = NS_TABLE_STATE_READY;
  265. // see if there are external fonts needed for certain chars in this table
  266. nsAutoCString key;
  267. nsAutoString value;
  268. for (int32_t i = 1; ; i++) {
  269. key.AssignLiteral("external.");
  270. key.AppendInt(i, 10);
  271. rv = mGlyphProperties->GetStringProperty(key, value);
  272. if (NS_FAILED(rv)) break;
  273. Clean(value);
  274. mGlyphCodeFonts.AppendElement(FontFamilyName(value, eUnquotedName)); // i.e., mGlyphCodeFonts[i] holds this font name
  275. }
  276. }
  277. // Update our cache if it is not associated to this character
  278. if (mCharCache != aChar) {
  279. // The key in the property file is interpreted as ASCII and kept
  280. // as such ...
  281. char key[10]; SprintfLiteral(key, "\\u%04X", aChar);
  282. nsAutoString value;
  283. nsresult rv = mGlyphProperties->GetStringProperty(nsDependentCString(key),
  284. value);
  285. if (NS_FAILED(rv)) return kNullGlyph;
  286. Clean(value);
  287. // See if this char uses external fonts; e.g., if the 2nd glyph is taken
  288. // from the external font '1', the property line looks like
  289. // \uNNNN = \uNNNN\uNNNN@1\uNNNN.
  290. // This is where mGlyphCache is pre-processed to explicitly store all glyph
  291. // codes as combined pairs of 'code@font', excluding the '@' separator. This
  292. // means that mGlyphCache[3*k],mGlyphCache[3*k+1] will later be rendered
  293. // with mGlyphCodeFonts[mGlyphCache[3*k+2]]
  294. // Note: font identifier is internally an ASCII digit to avoid the null
  295. // char issue
  296. nsAutoString buffer;
  297. int32_t length = value.Length();
  298. int32_t i = 0; // index in value
  299. while (i < length) {
  300. char16_t code = value[i];
  301. ++i;
  302. buffer.Append(code);
  303. // Read the next word if we have a non-BMP character.
  304. if (i < length && NS_IS_HIGH_SURROGATE(code)) {
  305. code = value[i];
  306. ++i;
  307. } else {
  308. code = char16_t('\0');
  309. }
  310. buffer.Append(code);
  311. // See if an external font is needed for the code point.
  312. // Limit of 9 external fonts
  313. char16_t font = 0;
  314. if (i+1 < length && value[i] == char16_t('@') &&
  315. value[i+1] >= char16_t('0') && value[i+1] <= char16_t('9')) {
  316. ++i;
  317. font = value[i] - '0';
  318. ++i;
  319. if (font >= mGlyphCodeFonts.Length()) {
  320. NS_ERROR("Nonexistent font referenced in glyph table");
  321. return kNullGlyph;
  322. }
  323. // The char cannot be handled if this font is not installed
  324. if (!mGlyphCodeFonts[font].mName.Length()) {
  325. return kNullGlyph;
  326. }
  327. }
  328. buffer.Append(font);
  329. }
  330. // update our cache with the new settings
  331. mGlyphCache.Assign(buffer);
  332. mCharCache = aChar;
  333. }
  334. // 3* is to account for the code@font pairs
  335. uint32_t index = 3*aPosition;
  336. if (index+2 >= mGlyphCache.Length()) return kNullGlyph;
  337. nsGlyphCode ch;
  338. ch.code[0] = mGlyphCache.CharAt(index);
  339. ch.code[1] = mGlyphCache.CharAt(index + 1);
  340. ch.font = mGlyphCache.CharAt(index + 2);
  341. return ch.code[0] == char16_t(0xFFFD) ? kNullGlyph : ch;
  342. }
  343. /* virtual */
  344. already_AddRefed<gfxTextRun>
  345. nsPropertiesTable::MakeTextRun(DrawTarget* aDrawTarget,
  346. int32_t aAppUnitsPerDevPixel,
  347. gfxFontGroup* aFontGroup,
  348. const nsGlyphCode& aGlyph)
  349. {
  350. NS_ASSERTION(!aGlyph.IsGlyphID(),
  351. "nsPropertiesTable can only access glyphs by code point");
  352. return aFontGroup->
  353. MakeTextRun(aGlyph.code, aGlyph.Length(), aDrawTarget,
  354. aAppUnitsPerDevPixel, 0, nullptr);
  355. }
  356. // An instance of nsOpenTypeTable is associated with one gfxFontEntry that
  357. // corresponds to an Open Type font with a MATH table. All the glyphs come from
  358. // the same font and the calls to access size variants and parts are directly
  359. // forwarded to the gfx code.
  360. class nsOpenTypeTable final : public nsGlyphTable {
  361. public:
  362. ~nsOpenTypeTable()
  363. {
  364. MOZ_COUNT_DTOR(nsOpenTypeTable);
  365. }
  366. virtual nsGlyphCode ElementAt(DrawTarget* aDrawTarget,
  367. int32_t aAppUnitsPerDevPixel,
  368. gfxFontGroup* aFontGroup,
  369. char16_t aChar,
  370. bool aVertical,
  371. uint32_t aPosition) override;
  372. virtual nsGlyphCode BigOf(DrawTarget* aDrawTarget,
  373. int32_t aAppUnitsPerDevPixel,
  374. gfxFontGroup* aFontGroup,
  375. char16_t aChar,
  376. bool aVertical,
  377. uint32_t aSize) override;
  378. virtual bool HasPartsOf(DrawTarget* aDrawTarget,
  379. int32_t aAppUnitsPerDevPixel,
  380. gfxFontGroup* aFontGroup,
  381. char16_t aChar,
  382. bool aVertical) override;
  383. const FontFamilyName&
  384. FontNameFor(const nsGlyphCode& aGlyphCode) const override {
  385. NS_ASSERTION(aGlyphCode.IsGlyphID(),
  386. "nsOpenTypeTable can only access glyphs by id");
  387. return mFontFamilyName;
  388. }
  389. virtual already_AddRefed<gfxTextRun>
  390. MakeTextRun(DrawTarget* aDrawTarget,
  391. int32_t aAppUnitsPerDevPixel,
  392. gfxFontGroup* aFontGroup,
  393. const nsGlyphCode& aGlyph) override;
  394. // This returns a new OpenTypeTable instance to give access to OpenType MATH
  395. // table or nullptr if the font does not have such table. Ownership is passed
  396. // to the caller.
  397. static nsOpenTypeTable* Create(gfxFont* aFont)
  398. {
  399. if (!aFont->TryGetMathTable()) {
  400. return nullptr;
  401. }
  402. return new nsOpenTypeTable(aFont);
  403. }
  404. private:
  405. RefPtr<gfxFont> mFont;
  406. FontFamilyName mFontFamilyName;
  407. uint32_t mGlyphID;
  408. explicit nsOpenTypeTable(gfxFont* aFont)
  409. : mFont(aFont),
  410. mFontFamilyName(aFont->GetFontEntry()->FamilyName(), eUnquotedName) {
  411. MOZ_COUNT_CTOR(nsOpenTypeTable);
  412. }
  413. void UpdateCache(DrawTarget* aDrawTarget,
  414. int32_t aAppUnitsPerDevPixel,
  415. gfxFontGroup* aFontGroup,
  416. char16_t aChar);
  417. };
  418. void
  419. nsOpenTypeTable::UpdateCache(DrawTarget* aDrawTarget,
  420. int32_t aAppUnitsPerDevPixel,
  421. gfxFontGroup* aFontGroup,
  422. char16_t aChar)
  423. {
  424. if (mCharCache != aChar) {
  425. RefPtr<gfxTextRun> textRun = aFontGroup->
  426. MakeTextRun(&aChar, 1, aDrawTarget, aAppUnitsPerDevPixel, 0, nullptr);
  427. const gfxTextRun::CompressedGlyph& data = textRun->GetCharacterGlyphs()[0];
  428. if (data.IsSimpleGlyph()) {
  429. mGlyphID = data.GetSimpleGlyph();
  430. } else if (data.GetGlyphCount() == 1) {
  431. mGlyphID = textRun->GetDetailedGlyphs(0)->mGlyphID;
  432. } else {
  433. mGlyphID = 0;
  434. }
  435. mCharCache = aChar;
  436. }
  437. }
  438. /* virtual */
  439. nsGlyphCode
  440. nsOpenTypeTable::ElementAt(DrawTarget* aDrawTarget,
  441. int32_t aAppUnitsPerDevPixel,
  442. gfxFontGroup* aFontGroup,
  443. char16_t aChar,
  444. bool aVertical,
  445. uint32_t aPosition)
  446. {
  447. UpdateCache(aDrawTarget, aAppUnitsPerDevPixel, aFontGroup, aChar);
  448. uint32_t parts[4];
  449. if (!mFont->MathTable()->VariantsParts(mGlyphID, aVertical, parts)) {
  450. return kNullGlyph;
  451. }
  452. uint32_t glyphID = parts[aPosition];
  453. if (!glyphID) {
  454. return kNullGlyph;
  455. }
  456. nsGlyphCode glyph;
  457. glyph.glyphID = glyphID;
  458. glyph.font = -1;
  459. return glyph;
  460. }
  461. /* virtual */
  462. nsGlyphCode
  463. nsOpenTypeTable::BigOf(DrawTarget* aDrawTarget,
  464. int32_t aAppUnitsPerDevPixel,
  465. gfxFontGroup* aFontGroup,
  466. char16_t aChar,
  467. bool aVertical,
  468. uint32_t aSize)
  469. {
  470. UpdateCache(aDrawTarget, aAppUnitsPerDevPixel, aFontGroup, aChar);
  471. uint32_t glyphID =
  472. mFont->MathTable()->VariantsSize(mGlyphID, aVertical, aSize);
  473. if (!glyphID) {
  474. return kNullGlyph;
  475. }
  476. nsGlyphCode glyph;
  477. glyph.glyphID = glyphID;
  478. glyph.font = -1;
  479. return glyph;
  480. }
  481. /* virtual */
  482. bool
  483. nsOpenTypeTable::HasPartsOf(DrawTarget* aDrawTarget,
  484. int32_t aAppUnitsPerDevPixel,
  485. gfxFontGroup* aFontGroup,
  486. char16_t aChar,
  487. bool aVertical)
  488. {
  489. UpdateCache(aDrawTarget, aAppUnitsPerDevPixel, aFontGroup, aChar);
  490. uint32_t parts[4];
  491. if (!mFont->MathTable()->VariantsParts(mGlyphID, aVertical, parts)) {
  492. return false;
  493. }
  494. return parts[0] || parts[1] || parts[2] || parts[3];
  495. }
  496. /* virtual */
  497. already_AddRefed<gfxTextRun>
  498. nsOpenTypeTable::MakeTextRun(DrawTarget* aDrawTarget,
  499. int32_t aAppUnitsPerDevPixel,
  500. gfxFontGroup* aFontGroup,
  501. const nsGlyphCode& aGlyph)
  502. {
  503. NS_ASSERTION(aGlyph.IsGlyphID(),
  504. "nsOpenTypeTable can only access glyphs by id");
  505. gfxTextRunFactory::Parameters params = {
  506. aDrawTarget, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevPixel
  507. };
  508. RefPtr<gfxTextRun> textRun =
  509. gfxTextRun::Create(&params, 1, aFontGroup, 0);
  510. textRun->AddGlyphRun(aFontGroup->GetFirstValidFont(),
  511. gfxTextRange::kFontGroup, 0,
  512. false, gfxTextRunFactory::TEXT_ORIENT_HORIZONTAL);
  513. // We don't care about CSS writing mode here;
  514. // math runs are assumed to be horizontal.
  515. gfxTextRun::DetailedGlyph detailedGlyph;
  516. detailedGlyph.mGlyphID = aGlyph.glyphID;
  517. detailedGlyph.mAdvance =
  518. NSToCoordRound(aAppUnitsPerDevPixel *
  519. aFontGroup->GetFirstValidFont()->
  520. GetGlyphHAdvance(aDrawTarget, aGlyph.glyphID));
  521. detailedGlyph.mXOffset = detailedGlyph.mYOffset = 0;
  522. gfxShapedText::CompressedGlyph g;
  523. g.SetComplex(true, true, 1);
  524. textRun->SetGlyphs(0, g, &detailedGlyph);
  525. return textRun.forget();
  526. }
  527. // -----------------------------------------------------------------------------
  528. // This is the list of all the applicable glyph tables.
  529. // We will maintain a single global instance that will only reveal those
  530. // glyph tables that are associated to fonts currently installed on the
  531. // user' system. The class is an XPCOM shutdown observer to allow us to
  532. // free its allocated data at shutdown
  533. class nsGlyphTableList final : public nsIObserver
  534. {
  535. public:
  536. NS_DECL_ISUPPORTS
  537. NS_DECL_NSIOBSERVER
  538. nsPropertiesTable mUnicodeTable;
  539. nsGlyphTableList()
  540. : mUnicodeTable(NS_LITERAL_STRING("Unicode"))
  541. {
  542. MOZ_COUNT_CTOR(nsGlyphTableList);
  543. }
  544. nsresult Initialize();
  545. nsresult Finalize();
  546. // Add a glyph table in the list, return the new table that was added
  547. nsGlyphTable*
  548. AddGlyphTable(const nsString& aPrimaryFontName);
  549. // Find the glyph table in the list corresponding to the given font family.
  550. nsGlyphTable*
  551. GetGlyphTableFor(const nsAString& aFamily);
  552. private:
  553. ~nsGlyphTableList()
  554. {
  555. MOZ_COUNT_DTOR(nsGlyphTableList);
  556. }
  557. nsPropertiesTable* PropertiesTableAt(int32_t aIndex) {
  558. return &mPropertiesTableList.ElementAt(aIndex);
  559. }
  560. int32_t PropertiesTableCount() {
  561. return mPropertiesTableList.Length();
  562. }
  563. // List of glyph tables;
  564. nsTArray<nsPropertiesTable> mPropertiesTableList;
  565. };
  566. NS_IMPL_ISUPPORTS(nsGlyphTableList, nsIObserver)
  567. // -----------------------------------------------------------------------------
  568. // Here is the global list of applicable glyph tables that we will be using
  569. static nsGlyphTableList* gGlyphTableList = nullptr;
  570. static bool gGlyphTableInitialized = false;
  571. // XPCOM shutdown observer
  572. NS_IMETHODIMP
  573. nsGlyphTableList::Observe(nsISupports* aSubject,
  574. const char* aTopic,
  575. const char16_t* someData)
  576. {
  577. Finalize();
  578. return NS_OK;
  579. }
  580. // Add an observer to XPCOM shutdown so that we can free our data at shutdown
  581. nsresult
  582. nsGlyphTableList::Initialize()
  583. {
  584. nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
  585. if (!obs)
  586. return NS_ERROR_FAILURE;
  587. nsresult rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
  588. NS_ENSURE_SUCCESS(rv, rv);
  589. return NS_OK;
  590. }
  591. // Remove our observer and free the memory that were allocated for us
  592. nsresult
  593. nsGlyphTableList::Finalize()
  594. {
  595. // Remove our observer from the observer service
  596. nsresult rv = NS_OK;
  597. nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
  598. if (obs)
  599. rv = obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
  600. else
  601. rv = NS_ERROR_FAILURE;
  602. gGlyphTableInitialized = false;
  603. // our oneself will be destroyed when our |Release| is called by the observer
  604. NS_IF_RELEASE(gGlyphTableList);
  605. return rv;
  606. }
  607. nsGlyphTable*
  608. nsGlyphTableList::AddGlyphTable(const nsString& aPrimaryFontName)
  609. {
  610. // See if there is already a special table for this family.
  611. nsGlyphTable* glyphTable = GetGlyphTableFor(aPrimaryFontName);
  612. if (glyphTable != &mUnicodeTable)
  613. return glyphTable;
  614. // allocate a table
  615. glyphTable = mPropertiesTableList.AppendElement(aPrimaryFontName);
  616. return glyphTable;
  617. }
  618. nsGlyphTable*
  619. nsGlyphTableList::GetGlyphTableFor(const nsAString& aFamily)
  620. {
  621. for (int32_t i = 0; i < PropertiesTableCount(); i++) {
  622. nsPropertiesTable* glyphTable = PropertiesTableAt(i);
  623. const FontFamilyName& primaryFontName = glyphTable->PrimaryFontName();
  624. nsAutoString primaryFontNameStr;
  625. primaryFontName.AppendToString(primaryFontNameStr);
  626. // TODO: would be nice to consider StripWhitespace and other aliasing
  627. if (primaryFontNameStr.Equals(aFamily, nsCaseInsensitiveStringComparator())) {
  628. return glyphTable;
  629. }
  630. }
  631. // Fall back to default Unicode table
  632. return &mUnicodeTable;
  633. }
  634. // -----------------------------------------------------------------------------
  635. static nsresult
  636. InitCharGlobals()
  637. {
  638. NS_ASSERTION(!gGlyphTableInitialized, "Error -- already initialized");
  639. gGlyphTableInitialized = true;
  640. // Allocate the placeholders for the preferred parts and variants
  641. nsresult rv = NS_ERROR_OUT_OF_MEMORY;
  642. RefPtr<nsGlyphTableList> glyphTableList = new nsGlyphTableList();
  643. if (glyphTableList) {
  644. rv = glyphTableList->Initialize();
  645. }
  646. if (NS_FAILED(rv)) {
  647. return rv;
  648. }
  649. // The gGlyphTableList has been successfully registered as a shutdown
  650. // observer and will be deleted at shutdown. We now add some private
  651. // per font-family tables for stretchy operators, in order of preference.
  652. // Do not include the Unicode table in this list.
  653. if (!glyphTableList->AddGlyphTable(NS_LITERAL_STRING("STIXGeneral"))
  654. #ifdef XP_WIN
  655. || !glyphTableList->AddGlyphTable(NS_LITERAL_STRING("Symbol"))
  656. #endif
  657. ) {
  658. rv = NS_ERROR_OUT_OF_MEMORY;
  659. }
  660. glyphTableList.forget(&gGlyphTableList);
  661. return rv;
  662. }
  663. // -----------------------------------------------------------------------------
  664. // And now the implementation of nsMathMLChar
  665. nsMathMLChar::~nsMathMLChar()
  666. {
  667. MOZ_COUNT_DTOR(nsMathMLChar);
  668. mStyleContext->Release();
  669. }
  670. nsStyleContext*
  671. nsMathMLChar::GetStyleContext() const
  672. {
  673. NS_ASSERTION(mStyleContext, "chars should always have style context");
  674. return mStyleContext;
  675. }
  676. void
  677. nsMathMLChar::SetStyleContext(nsStyleContext* aStyleContext)
  678. {
  679. NS_PRECONDITION(aStyleContext, "null ptr");
  680. if (aStyleContext != mStyleContext) {
  681. if (mStyleContext)
  682. mStyleContext->Release();
  683. if (aStyleContext) {
  684. mStyleContext = aStyleContext;
  685. aStyleContext->AddRef();
  686. }
  687. }
  688. }
  689. void
  690. nsMathMLChar::SetData(nsString& aData)
  691. {
  692. if (!gGlyphTableInitialized) {
  693. InitCharGlobals();
  694. }
  695. mData = aData;
  696. // some assumptions until proven otherwise
  697. // note that mGlyph is not initialized
  698. mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
  699. mBoundingMetrics = nsBoundingMetrics();
  700. // check if stretching is applicable ...
  701. if (gGlyphTableList && (1 == mData.Length())) {
  702. mDirection = nsMathMLOperators::GetStretchyDirection(mData);
  703. // default tentative table (not the one that is necessarily going
  704. // to be used)
  705. }
  706. }
  707. // -----------------------------------------------------------------------------
  708. /*
  709. The Stretch:
  710. @param aContainerSize - suggested size for the stretched char
  711. @param aDesiredStretchSize - OUT parameter. The desired size
  712. after stretching. If no stretching is done, the output will
  713. simply give the base size.
  714. How it works?
  715. Summary:-
  716. The Stretch() method first looks for a glyph of appropriate
  717. size; If a glyph is found, it is cached by this object and
  718. its size is returned in aDesiredStretchSize. The cached
  719. glyph will then be used at the painting stage.
  720. If no glyph of appropriate size is found, a search is made
  721. to see if the char can be built by parts.
  722. Details:-
  723. A character gets stretched through the following pipeline :
  724. 1) If the base size of the char is sufficient to cover the
  725. container' size, we use that. If not, it will still be
  726. used as a fallback if the other stages in the pipeline fail.
  727. Issues :
  728. a) The base size, the parts and the variants of a char can
  729. be in different fonts. For eg., the base size for '(' should
  730. come from a normal ascii font if CMEX10 is used, since CMEX10
  731. only contains the stretched versions. Hence, there are two
  732. style contexts in use throughout the process. The leaf style
  733. context of the char holds fonts with which to try to stretch
  734. the char. The parent style context of the char contains fonts
  735. for normal rendering. So the parent context is the one used
  736. to get the initial base size at the start of the pipeline.
  737. b) For operators that can be largeop's in display mode,
  738. we will skip the base size even if it fits, so that
  739. the next stage in the pipeline is given a chance to find
  740. a largeop variant. If the next stage fails, we fallback
  741. to the base size.
  742. 2) We search for the first larger variant of the char that fits the
  743. container' size. We first search for larger variants using the glyph
  744. table corresponding to the first existing font specified in the list of
  745. stretchy fonts held by the leaf style context (from -moz-math-stretchy in
  746. mathml.css). Generic fonts are resolved by the preference
  747. "font.mathfont-family".
  748. Issues :
  749. a) the largeop and display settings determine the starting
  750. size when we do the above search, regardless of whether
  751. smaller variants already fit the container' size.
  752. b) if it is a largeopOnly request (i.e., a displaystyle operator
  753. with largeop=true and stretchy=false), we break after finding
  754. the first starting variant, regardless of whether that
  755. variant fits the container's size.
  756. 3) If a variant of appropriate size wasn't found, we see if the char
  757. can be built by parts using the same glyph table.
  758. Issue:
  759. There are chars that have no middle and glue glyphs. For
  760. such chars, the parts need to be joined using the rule.
  761. By convention (TeXbook p.225), the descent of the parts is
  762. zero while their ascent gives the thickness of the rule that
  763. should be used to join them.
  764. 4) If a match was not found in that glyph table, repeat from 2 to search the
  765. ordered list of stretchy fonts for the first font with a glyph table that
  766. provides a fit to the container size. If no fit is found, the closest fit
  767. is used.
  768. Of note:
  769. When the pipeline completes successfully, the desired size of the
  770. stretched char can actually be slightly larger or smaller than
  771. aContainerSize. But it is the responsibility of the caller to
  772. account for the spacing when setting aContainerSize, and to leave
  773. any extra margin when placing the stretched char.
  774. */
  775. // -----------------------------------------------------------------------------
  776. // plain TeX settings (TeXbook p.152)
  777. #define NS_MATHML_DELIMITER_FACTOR 0.901f
  778. #define NS_MATHML_DELIMITER_SHORTFALL_POINTS 5.0f
  779. static bool
  780. IsSizeOK(nscoord a, nscoord b, uint32_t aHint)
  781. {
  782. // Normal: True if 'a' is around +/-10% of the target 'b' (10% is
  783. // 1-DelimiterFactor). This often gives a chance to the base size to
  784. // win, especially in the context of <mfenced> without tall elements
  785. // or in sloppy markups without protective <mrow></mrow>
  786. bool isNormal =
  787. (aHint & NS_STRETCH_NORMAL) &&
  788. Abs<float>(a - b) < (1.0f - NS_MATHML_DELIMITER_FACTOR) * float(b);
  789. // Nearer: True if 'a' is around max{ +/-10% of 'b' , 'b' - 5pt },
  790. // as documented in The TeXbook, Ch.17, p.152.
  791. // i.e. within 10% and within 5pt
  792. bool isNearer = false;
  793. if (aHint & (NS_STRETCH_NEARER | NS_STRETCH_LARGEOP)) {
  794. float c = std::max(float(b) * NS_MATHML_DELIMITER_FACTOR,
  795. float(b) - nsPresContext::
  796. CSSPointsToAppUnits(NS_MATHML_DELIMITER_SHORTFALL_POINTS));
  797. isNearer = Abs<float>(b - a) <= float(b) - c;
  798. }
  799. // Smaller: Mainly for transitory use, to compare two candidate
  800. // choices
  801. bool isSmaller =
  802. (aHint & NS_STRETCH_SMALLER) &&
  803. float(a) >= NS_MATHML_DELIMITER_FACTOR * float(b) &&
  804. a <= b;
  805. // Larger: Critical to the sqrt code to ensure that the radical
  806. // size is tall enough
  807. bool isLarger =
  808. (aHint & (NS_STRETCH_LARGER | NS_STRETCH_LARGEOP)) &&
  809. a >= b;
  810. return (isNormal || isSmaller || isNearer || isLarger);
  811. }
  812. static bool
  813. IsSizeBetter(nscoord a, nscoord olda, nscoord b, uint32_t aHint)
  814. {
  815. if (0 == olda)
  816. return true;
  817. if (aHint & (NS_STRETCH_LARGER | NS_STRETCH_LARGEOP))
  818. return (a >= olda) ? (olda < b) : (a >= b);
  819. if (aHint & NS_STRETCH_SMALLER)
  820. return (a <= olda) ? (olda > b) : (a <= b);
  821. // XXXkt prob want log scale here i.e. 1.5 is closer to 1 than 0.5
  822. return Abs(a - b) < Abs(olda - b);
  823. }
  824. // We want to place the glyphs even when they don't fit at their
  825. // full extent, i.e., we may clip to tolerate a small amount of
  826. // overlap between the parts. This is important to cater for fonts
  827. // with long glues.
  828. static nscoord
  829. ComputeSizeFromParts(nsPresContext* aPresContext,
  830. nsGlyphCode* aGlyphs,
  831. nscoord* aSizes,
  832. nscoord aTargetSize)
  833. {
  834. enum {first, middle, last, glue};
  835. // Add the parts that cannot be left out.
  836. nscoord sum = 0;
  837. for (int32_t i = first; i <= last; i++) {
  838. sum += aSizes[i];
  839. }
  840. // Determine how much is used in joins
  841. nscoord oneDevPixel = aPresContext->AppUnitsPerDevPixel();
  842. int32_t joins = aGlyphs[middle] == aGlyphs[glue] ? 1 : 2;
  843. // Pick a maximum size using a maximum number of glue glyphs that we are
  844. // prepared to draw for one character.
  845. const int32_t maxGlyphs = 1000;
  846. // This also takes into account the fact that, if the glue has no size,
  847. // then the character can't be lengthened.
  848. nscoord maxSize = sum - 2 * joins * oneDevPixel + maxGlyphs * aSizes[glue];
  849. if (maxSize < aTargetSize)
  850. return maxSize; // settle with the maximum size
  851. // Get the minimum allowable size using some flex.
  852. nscoord minSize = NSToCoordRound(NS_MATHML_DELIMITER_FACTOR * sum);
  853. if (minSize > aTargetSize)
  854. return minSize; // settle with the minimum size
  855. // Fill-up the target area
  856. return aTargetSize;
  857. }
  858. // Update the font if there is a family change and returns the font group.
  859. bool
  860. nsMathMLChar::SetFontFamily(nsPresContext* aPresContext,
  861. const nsGlyphTable* aGlyphTable,
  862. const nsGlyphCode& aGlyphCode,
  863. const FontFamilyList& aDefaultFamilyList,
  864. nsFont& aFont,
  865. RefPtr<gfxFontGroup>* aFontGroup)
  866. {
  867. FontFamilyList glyphCodeFont;
  868. if (aGlyphCode.font) {
  869. glyphCodeFont.Append(aGlyphTable->FontNameFor(aGlyphCode));
  870. }
  871. const FontFamilyList& familyList =
  872. aGlyphCode.font ? glyphCodeFont : aDefaultFamilyList;
  873. if (!*aFontGroup || !(aFont.fontlist == familyList)) {
  874. nsFont font = aFont;
  875. font.fontlist = familyList;
  876. const nsStyleFont* styleFont = mStyleContext->StyleFont();
  877. nsFontMetrics::Params params;
  878. params.language = styleFont->mLanguage;
  879. params.explicitLanguage = styleFont->mExplicitLanguage;
  880. params.userFontSet = aPresContext->GetUserFontSet();
  881. params.textPerf = aPresContext->GetTextPerfMetrics();
  882. RefPtr<nsFontMetrics> fm =
  883. aPresContext->DeviceContext()->GetMetricsFor(font, params);
  884. // Set the font if it is an unicode table
  885. // or if the same family name has been found
  886. gfxFont *firstFont = fm->GetThebesFontGroup()->GetFirstValidFont();
  887. FontFamilyList firstFontList;
  888. firstFontList.Append(
  889. FontFamilyName(firstFont->GetFontEntry()->FamilyName(), eUnquotedName));
  890. if (aGlyphTable == &gGlyphTableList->mUnicodeTable ||
  891. firstFontList == familyList) {
  892. aFont.fontlist = familyList;
  893. *aFontGroup = fm->GetThebesFontGroup();
  894. } else {
  895. return false; // We did not set the font
  896. }
  897. }
  898. return true;
  899. }
  900. static nsBoundingMetrics
  901. MeasureTextRun(DrawTarget* aDrawTarget, gfxTextRun* aTextRun)
  902. {
  903. gfxTextRun::Metrics metrics =
  904. aTextRun->MeasureText(gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS, aDrawTarget);
  905. nsBoundingMetrics bm;
  906. bm.leftBearing = NSToCoordFloor(metrics.mBoundingBox.X());
  907. bm.rightBearing = NSToCoordCeil(metrics.mBoundingBox.XMost());
  908. bm.ascent = NSToCoordCeil(-metrics.mBoundingBox.Y());
  909. bm.descent = NSToCoordCeil(metrics.mBoundingBox.YMost());
  910. bm.width = NSToCoordRound(metrics.mAdvanceWidth);
  911. return bm;
  912. }
  913. class nsMathMLChar::StretchEnumContext {
  914. public:
  915. StretchEnumContext(nsMathMLChar* aChar,
  916. nsPresContext* aPresContext,
  917. DrawTarget* aDrawTarget,
  918. float aFontSizeInflation,
  919. nsStretchDirection aStretchDirection,
  920. nscoord aTargetSize,
  921. uint32_t aStretchHint,
  922. nsBoundingMetrics& aStretchedMetrics,
  923. const FontFamilyList& aFamilyList,
  924. bool& aGlyphFound)
  925. : mChar(aChar),
  926. mPresContext(aPresContext),
  927. mDrawTarget(aDrawTarget),
  928. mFontSizeInflation(aFontSizeInflation),
  929. mDirection(aStretchDirection),
  930. mTargetSize(aTargetSize),
  931. mStretchHint(aStretchHint),
  932. mBoundingMetrics(aStretchedMetrics),
  933. mFamilyList(aFamilyList),
  934. mTryVariants(true),
  935. mTryParts(true),
  936. mGlyphFound(aGlyphFound) {}
  937. static bool
  938. EnumCallback(const FontFamilyName& aFamily, bool aGeneric, void *aData);
  939. private:
  940. bool TryVariants(nsGlyphTable* aGlyphTable,
  941. RefPtr<gfxFontGroup>* aFontGroup,
  942. const FontFamilyList& aFamilyList);
  943. bool TryParts(nsGlyphTable* aGlyphTable,
  944. RefPtr<gfxFontGroup>* aFontGroup,
  945. const FontFamilyList& aFamilyList);
  946. nsMathMLChar* mChar;
  947. nsPresContext* mPresContext;
  948. DrawTarget* mDrawTarget;
  949. float mFontSizeInflation;
  950. const nsStretchDirection mDirection;
  951. const nscoord mTargetSize;
  952. const uint32_t mStretchHint;
  953. nsBoundingMetrics& mBoundingMetrics;
  954. // Font families to search
  955. const FontFamilyList& mFamilyList;
  956. public:
  957. bool mTryVariants;
  958. bool mTryParts;
  959. private:
  960. AutoTArray<nsGlyphTable*,16> mTablesTried;
  961. bool& mGlyphFound;
  962. };
  963. // 2. See if there are any glyphs of the appropriate size.
  964. // Returns true if the size is OK, false to keep searching.
  965. // Always updates the char if a better match is found.
  966. bool
  967. nsMathMLChar::
  968. StretchEnumContext::TryVariants(nsGlyphTable* aGlyphTable,
  969. RefPtr<gfxFontGroup>* aFontGroup,
  970. const FontFamilyList& aFamilyList)
  971. {
  972. // Use our stretchy style context now that stretching is in progress
  973. nsStyleContext *sc = mChar->mStyleContext;
  974. nsFont font = sc->StyleFont()->mFont;
  975. NormalizeDefaultFont(font, mFontSizeInflation);
  976. bool isVertical = (mDirection == NS_STRETCH_DIRECTION_VERTICAL);
  977. nscoord oneDevPixel = mPresContext->AppUnitsPerDevPixel();
  978. char16_t uchar = mChar->mData[0];
  979. bool largeop = (NS_STRETCH_LARGEOP & mStretchHint) != 0;
  980. bool largeopOnly =
  981. largeop && (NS_STRETCH_VARIABLE_MASK & mStretchHint) == 0;
  982. bool maxWidth = (NS_STRETCH_MAXWIDTH & mStretchHint) != 0;
  983. nscoord bestSize =
  984. isVertical ? mBoundingMetrics.ascent + mBoundingMetrics.descent
  985. : mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing;
  986. bool haveBetter = false;
  987. // start at size = 1 (size = 0 is the char at its normal size)
  988. int32_t size = 1;
  989. nsGlyphCode ch;
  990. nscoord displayOperatorMinHeight = 0;
  991. if (largeopOnly) {
  992. NS_ASSERTION(isVertical, "Stretching should be in the vertical direction");
  993. ch = aGlyphTable->BigOf(mDrawTarget, oneDevPixel, *aFontGroup, uchar,
  994. isVertical, 0);
  995. if (ch.IsGlyphID()) {
  996. gfxFont* mathFont = aFontGroup->get()->GetFirstMathFont();
  997. // For OpenType MATH fonts, we will rely on the DisplayOperatorMinHeight
  998. // to select the right size variant. Note that the value is sometimes too
  999. // small so we use kLargeOpFactor/kIntegralFactor as a minimum value.
  1000. if (mathFont) {
  1001. displayOperatorMinHeight = mathFont->MathTable()->
  1002. Constant(gfxMathTable::DisplayOperatorMinHeight, oneDevPixel);
  1003. RefPtr<gfxTextRun> textRun =
  1004. aGlyphTable->MakeTextRun(mDrawTarget, oneDevPixel, *aFontGroup, ch);
  1005. nsBoundingMetrics bm = MeasureTextRun(mDrawTarget, textRun.get());
  1006. float largeopFactor = kLargeOpFactor;
  1007. if (NS_STRETCH_INTEGRAL & mStretchHint) {
  1008. // integrals are drawn taller
  1009. largeopFactor = kIntegralFactor;
  1010. }
  1011. nscoord minHeight = largeopFactor * (bm.ascent + bm.descent);
  1012. if (displayOperatorMinHeight < minHeight) {
  1013. displayOperatorMinHeight = minHeight;
  1014. }
  1015. }
  1016. }
  1017. }
  1018. #ifdef NOISY_SEARCH
  1019. printf(" searching in %s ...\n",
  1020. NS_LossyConvertUTF16toASCII(aFamily).get());
  1021. #endif
  1022. while ((ch = aGlyphTable->BigOf(mDrawTarget, oneDevPixel, *aFontGroup,
  1023. uchar, isVertical, size)).Exists()) {
  1024. if (!mChar->SetFontFamily(mPresContext, aGlyphTable, ch, aFamilyList, font,
  1025. aFontGroup)) {
  1026. // if largeopOnly is set, break now
  1027. if (largeopOnly) break;
  1028. ++size;
  1029. continue;
  1030. }
  1031. RefPtr<gfxTextRun> textRun =
  1032. aGlyphTable->MakeTextRun(mDrawTarget, oneDevPixel, *aFontGroup, ch);
  1033. nsBoundingMetrics bm = MeasureTextRun(mDrawTarget, textRun.get());
  1034. if (ch.IsGlyphID()) {
  1035. gfxFont* mathFont = aFontGroup->get()->GetFirstMathFont();
  1036. if (mathFont) {
  1037. // MeasureTextRun should have set the advance width to the right
  1038. // bearing for OpenType MATH fonts. We now subtract the italic
  1039. // correction, so that nsMathMLmmultiscripts will place the scripts
  1040. // correctly.
  1041. // Note that STIX-Word does not provide italic corrections but its
  1042. // advance widths do not match right bearings.
  1043. // (http://sourceforge.net/p/stixfonts/tracking/50/)
  1044. gfxFloat italicCorrection =
  1045. mathFont->MathTable()->ItalicsCorrection(ch.glyphID);
  1046. if (italicCorrection) {
  1047. bm.width -=
  1048. NSToCoordRound(italicCorrection * oneDevPixel);
  1049. if (bm.width < 0) {
  1050. bm.width = 0;
  1051. }
  1052. }
  1053. }
  1054. }
  1055. nscoord charSize =
  1056. isVertical ? bm.ascent + bm.descent
  1057. : bm.rightBearing - bm.leftBearing;
  1058. if (largeopOnly ||
  1059. IsSizeBetter(charSize, bestSize, mTargetSize, mStretchHint)) {
  1060. mGlyphFound = true;
  1061. if (maxWidth) {
  1062. // IsSizeBetter() checked that charSize < maxsize;
  1063. // Leave ascent, descent, and bestsize as these contain maxsize.
  1064. if (mBoundingMetrics.width < bm.width)
  1065. mBoundingMetrics.width = bm.width;
  1066. if (mBoundingMetrics.leftBearing > bm.leftBearing)
  1067. mBoundingMetrics.leftBearing = bm.leftBearing;
  1068. if (mBoundingMetrics.rightBearing < bm.rightBearing)
  1069. mBoundingMetrics.rightBearing = bm.rightBearing;
  1070. // Continue to check other sizes unless largeopOnly
  1071. haveBetter = largeopOnly;
  1072. }
  1073. else {
  1074. mBoundingMetrics = bm;
  1075. haveBetter = true;
  1076. bestSize = charSize;
  1077. mChar->mGlyphs[0] = Move(textRun);
  1078. mChar->mDraw = DRAW_VARIANT;
  1079. }
  1080. #ifdef NOISY_SEARCH
  1081. printf(" size:%d Current best\n", size);
  1082. #endif
  1083. }
  1084. else {
  1085. #ifdef NOISY_SEARCH
  1086. printf(" size:%d Rejected!\n", size);
  1087. #endif
  1088. if (haveBetter)
  1089. break; // Not making an futher progress, stop searching
  1090. }
  1091. // If this a largeop only operator, we stop if the glyph is large enough.
  1092. if (largeopOnly && (bm.ascent + bm.descent) >= displayOperatorMinHeight) {
  1093. break;
  1094. }
  1095. ++size;
  1096. }
  1097. return haveBetter &&
  1098. (largeopOnly || IsSizeOK(bestSize, mTargetSize, mStretchHint));
  1099. }
  1100. // 3. Build by parts.
  1101. // Returns true if the size is OK, false to keep searching.
  1102. // Always updates the char if a better match is found.
  1103. bool
  1104. nsMathMLChar::StretchEnumContext::TryParts(nsGlyphTable* aGlyphTable,
  1105. RefPtr<gfxFontGroup>* aFontGroup,
  1106. const FontFamilyList& aFamilyList)
  1107. {
  1108. // Use our stretchy style context now that stretching is in progress
  1109. nsFont font = mChar->mStyleContext->StyleFont()->mFont;
  1110. NormalizeDefaultFont(font, mFontSizeInflation);
  1111. // Compute the bounding metrics of all partial glyphs
  1112. RefPtr<gfxTextRun> textRun[4];
  1113. nsGlyphCode chdata[4];
  1114. nsBoundingMetrics bmdata[4];
  1115. nscoord sizedata[4];
  1116. bool isVertical = (mDirection == NS_STRETCH_DIRECTION_VERTICAL);
  1117. nscoord oneDevPixel = mPresContext->AppUnitsPerDevPixel();
  1118. char16_t uchar = mChar->mData[0];
  1119. bool maxWidth = (NS_STRETCH_MAXWIDTH & mStretchHint) != 0;
  1120. if (!aGlyphTable->HasPartsOf(mDrawTarget, oneDevPixel, *aFontGroup,
  1121. uchar, isVertical))
  1122. return false; // to next table
  1123. for (int32_t i = 0; i < 4; i++) {
  1124. nsGlyphCode ch = aGlyphTable->ElementAt(mDrawTarget, oneDevPixel,
  1125. *aFontGroup, uchar, isVertical, i);
  1126. chdata[i] = ch;
  1127. if (ch.Exists()) {
  1128. if (!mChar->SetFontFamily(mPresContext, aGlyphTable, ch, aFamilyList, font,
  1129. aFontGroup))
  1130. return false;
  1131. textRun[i] = aGlyphTable->MakeTextRun(mDrawTarget, oneDevPixel,
  1132. *aFontGroup, ch);
  1133. nsBoundingMetrics bm = MeasureTextRun(mDrawTarget, textRun[i].get());
  1134. bmdata[i] = bm;
  1135. sizedata[i] = isVertical ? bm.ascent + bm.descent
  1136. : bm.rightBearing - bm.leftBearing;
  1137. } else {
  1138. // Null glue indicates that a rule will be drawn, which can stretch to
  1139. // fill any space.
  1140. textRun[i] = nullptr;
  1141. bmdata[i] = nsBoundingMetrics();
  1142. sizedata[i] = i == 3 ? mTargetSize : 0;
  1143. }
  1144. }
  1145. // For the Unicode table, we check that all the glyphs are actually found and
  1146. // come from the same font.
  1147. if (aGlyphTable == &gGlyphTableList->mUnicodeTable) {
  1148. gfxFont* unicodeFont = nullptr;
  1149. for (int32_t i = 0; i < 4; i++) {
  1150. if (!textRun[i]) {
  1151. continue;
  1152. }
  1153. if (textRun[i]->GetLength() != 1 ||
  1154. textRun[i]->GetCharacterGlyphs()[0].IsMissing()) {
  1155. return false;
  1156. }
  1157. uint32_t numGlyphRuns;
  1158. const gfxTextRun::GlyphRun* glyphRuns =
  1159. textRun[i]->GetGlyphRuns(&numGlyphRuns);
  1160. if (numGlyphRuns != 1) {
  1161. return false;
  1162. }
  1163. if (!unicodeFont) {
  1164. unicodeFont = glyphRuns[0].mFont;
  1165. } else if (unicodeFont != glyphRuns[0].mFont) {
  1166. return false;
  1167. }
  1168. }
  1169. }
  1170. // Build by parts if we have successfully computed the
  1171. // bounding metrics of all parts.
  1172. nscoord computedSize = ComputeSizeFromParts(mPresContext, chdata, sizedata,
  1173. mTargetSize);
  1174. nscoord currentSize =
  1175. isVertical ? mBoundingMetrics.ascent + mBoundingMetrics.descent
  1176. : mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing;
  1177. if (!IsSizeBetter(computedSize, currentSize, mTargetSize, mStretchHint)) {
  1178. #ifdef NOISY_SEARCH
  1179. printf(" Font %s Rejected!\n",
  1180. NS_LossyConvertUTF16toASCII(fontName).get());
  1181. #endif
  1182. return false; // to next table
  1183. }
  1184. #ifdef NOISY_SEARCH
  1185. printf(" Font %s Current best!\n",
  1186. NS_LossyConvertUTF16toASCII(fontName).get());
  1187. #endif
  1188. // The computed size is the best we have found so far...
  1189. // now is the time to compute and cache our bounding metrics
  1190. if (isVertical) {
  1191. int32_t i;
  1192. // Try and find the first existing part and then determine the extremal
  1193. // horizontal metrics of the parts.
  1194. for (i = 0; i <= 3 && !textRun[i]; i++);
  1195. if (i == 4) {
  1196. NS_ERROR("Cannot stretch - All parts missing");
  1197. return false;
  1198. }
  1199. nscoord lbearing = bmdata[i].leftBearing;
  1200. nscoord rbearing = bmdata[i].rightBearing;
  1201. nscoord width = bmdata[i].width;
  1202. i++;
  1203. for (; i <= 3; i++) {
  1204. if (!textRun[i]) continue;
  1205. lbearing = std::min(lbearing, bmdata[i].leftBearing);
  1206. rbearing = std::max(rbearing, bmdata[i].rightBearing);
  1207. width = std::max(width, bmdata[i].width);
  1208. }
  1209. if (maxWidth) {
  1210. lbearing = std::min(lbearing, mBoundingMetrics.leftBearing);
  1211. rbearing = std::max(rbearing, mBoundingMetrics.rightBearing);
  1212. width = std::max(width, mBoundingMetrics.width);
  1213. }
  1214. mBoundingMetrics.width = width;
  1215. // When maxWidth, updating ascent and descent indicates that no characters
  1216. // larger than this character's minimum size need to be checked as they
  1217. // will not be used.
  1218. mBoundingMetrics.ascent = bmdata[0].ascent; // not used except with descent
  1219. // for height
  1220. mBoundingMetrics.descent = computedSize - mBoundingMetrics.ascent;
  1221. mBoundingMetrics.leftBearing = lbearing;
  1222. mBoundingMetrics.rightBearing = rbearing;
  1223. }
  1224. else {
  1225. int32_t i;
  1226. // Try and find the first existing part and then determine the extremal
  1227. // vertical metrics of the parts.
  1228. for (i = 0; i <= 3 && !textRun[i]; i++);
  1229. if (i == 4) {
  1230. NS_ERROR("Cannot stretch - All parts missing");
  1231. return false;
  1232. }
  1233. nscoord ascent = bmdata[i].ascent;
  1234. nscoord descent = bmdata[i].descent;
  1235. i++;
  1236. for (; i <= 3; i++) {
  1237. if (!textRun[i]) continue;
  1238. ascent = std::max(ascent, bmdata[i].ascent);
  1239. descent = std::max(descent, bmdata[i].descent);
  1240. }
  1241. mBoundingMetrics.width = computedSize;
  1242. mBoundingMetrics.ascent = ascent;
  1243. mBoundingMetrics.descent = descent;
  1244. mBoundingMetrics.leftBearing = 0;
  1245. mBoundingMetrics.rightBearing = computedSize;
  1246. }
  1247. mGlyphFound = true;
  1248. if (maxWidth)
  1249. return false; // Continue to check other sizes
  1250. // reset
  1251. mChar->mDraw = DRAW_PARTS;
  1252. for (int32_t i = 0; i < 4; i++) {
  1253. mChar->mGlyphs[i] = Move(textRun[i]);
  1254. mChar->mBmData[i] = bmdata[i];
  1255. }
  1256. return IsSizeOK(computedSize, mTargetSize, mStretchHint);
  1257. }
  1258. // This is called for each family, whether it exists or not
  1259. bool
  1260. nsMathMLChar::StretchEnumContext::EnumCallback(const FontFamilyName& aFamily,
  1261. bool aGeneric, void *aData)
  1262. {
  1263. StretchEnumContext* context = static_cast<StretchEnumContext*>(aData);
  1264. // for comparisons, force use of unquoted names
  1265. FontFamilyName unquotedFamilyName(aFamily);
  1266. if (unquotedFamilyName.mType == eFamily_named_quoted) {
  1267. unquotedFamilyName.mType = eFamily_named;
  1268. }
  1269. // Check font family if it is not a generic one
  1270. // We test with the kNullGlyph
  1271. nsStyleContext *sc = context->mChar->mStyleContext;
  1272. nsFont font = sc->StyleFont()->mFont;
  1273. NormalizeDefaultFont(font, context->mFontSizeInflation);
  1274. RefPtr<gfxFontGroup> fontGroup;
  1275. FontFamilyList family;
  1276. family.Append(unquotedFamilyName);
  1277. if (!aGeneric && !context->mChar->SetFontFamily(context->mPresContext,
  1278. nullptr, kNullGlyph, family,
  1279. font, &fontGroup))
  1280. return true; // Could not set the family
  1281. // Determine the glyph table to use for this font.
  1282. nsAutoPtr<nsOpenTypeTable> openTypeTable;
  1283. nsGlyphTable* glyphTable;
  1284. if (aGeneric) {
  1285. // This is a generic font, use the Unicode table.
  1286. glyphTable = &gGlyphTableList->mUnicodeTable;
  1287. } else {
  1288. // If the font contains an Open Type MATH table, use it.
  1289. openTypeTable = nsOpenTypeTable::Create(fontGroup->GetFirstValidFont());
  1290. if (openTypeTable) {
  1291. glyphTable = openTypeTable;
  1292. } else {
  1293. // Otherwise try to find a .properties file corresponding to that font
  1294. // family or fallback to the Unicode table.
  1295. nsAutoString familyName;
  1296. unquotedFamilyName.AppendToString(familyName);
  1297. glyphTable = gGlyphTableList->GetGlyphTableFor(familyName);
  1298. }
  1299. }
  1300. if (!openTypeTable) {
  1301. if (context->mTablesTried.Contains(glyphTable))
  1302. return true; // already tried this one
  1303. // Only try this table once.
  1304. context->mTablesTried.AppendElement(glyphTable);
  1305. }
  1306. // If the unicode table is being used, then search all font families. If a
  1307. // special table is being used then the font in this family should have the
  1308. // specified glyphs.
  1309. const FontFamilyList& familyList = glyphTable == &gGlyphTableList->mUnicodeTable ?
  1310. context->mFamilyList : family;
  1311. if((context->mTryVariants &&
  1312. context->TryVariants(glyphTable, &fontGroup, familyList)) ||
  1313. (context->mTryParts && context->TryParts(glyphTable,
  1314. &fontGroup,
  1315. familyList)))
  1316. return false; // no need to continue
  1317. return true; // true means continue
  1318. }
  1319. // insert math fallback families just before the first generic or at the end
  1320. // when no generic present
  1321. static void
  1322. InsertMathFallbacks(FontFamilyList& aFamilyList,
  1323. nsTArray<nsString>& aFallbacks)
  1324. {
  1325. FontFamilyList aMergedList;
  1326. bool inserted = false;
  1327. const nsTArray<FontFamilyName>& fontlist = aFamilyList.GetFontlist();
  1328. uint32_t i, num = fontlist.Length();
  1329. for (i = 0; i < num; i++) {
  1330. const FontFamilyName& name = fontlist[i];
  1331. if (!inserted && name.IsGeneric()) {
  1332. inserted = true;
  1333. aMergedList.Append(aFallbacks);
  1334. }
  1335. aMergedList.Append(name);
  1336. }
  1337. if (!inserted) {
  1338. aMergedList.Append(aFallbacks);
  1339. }
  1340. aFamilyList = aMergedList;
  1341. }
  1342. nsresult
  1343. nsMathMLChar::StretchInternal(nsPresContext* aPresContext,
  1344. DrawTarget* aDrawTarget,
  1345. float aFontSizeInflation,
  1346. nsStretchDirection& aStretchDirection,
  1347. const nsBoundingMetrics& aContainerSize,
  1348. nsBoundingMetrics& aDesiredStretchSize,
  1349. uint32_t aStretchHint,
  1350. // These are currently only used when
  1351. // aStretchHint & NS_STRETCH_MAXWIDTH:
  1352. float aMaxSize,
  1353. bool aMaxSizeIsAbsolute)
  1354. {
  1355. // if we have been called before, and we didn't actually stretch, our
  1356. // direction may have been set to NS_STRETCH_DIRECTION_UNSUPPORTED.
  1357. // So first set our direction back to its instrinsic value
  1358. nsStretchDirection direction = nsMathMLOperators::GetStretchyDirection(mData);
  1359. // Set default font and get the default bounding metrics
  1360. // mStyleContext is a leaf context used only when stretching happens.
  1361. // For the base size, the default font should come from the parent context
  1362. nsFont font = mStyleContext->GetParent()->StyleFont()->mFont;
  1363. NormalizeDefaultFont(font, aFontSizeInflation);
  1364. const nsStyleFont* styleFont = mStyleContext->StyleFont();
  1365. nsFontMetrics::Params params;
  1366. params.language = styleFont->mLanguage;
  1367. params.explicitLanguage = styleFont->mExplicitLanguage;
  1368. params.userFontSet = aPresContext->GetUserFontSet();
  1369. params.textPerf = aPresContext->GetTextPerfMetrics();
  1370. RefPtr<nsFontMetrics> fm =
  1371. aPresContext->DeviceContext()->GetMetricsFor(font, params);
  1372. uint32_t len = uint32_t(mData.Length());
  1373. mGlyphs[0] = fm->GetThebesFontGroup()->
  1374. MakeTextRun(static_cast<const char16_t*>(mData.get()), len, aDrawTarget,
  1375. aPresContext->AppUnitsPerDevPixel(), 0,
  1376. aPresContext->MissingFontRecorder());
  1377. aDesiredStretchSize = MeasureTextRun(aDrawTarget, mGlyphs[0].get());
  1378. bool maxWidth = (NS_STRETCH_MAXWIDTH & aStretchHint) != 0;
  1379. if (!maxWidth) {
  1380. mUnscaledAscent = aDesiredStretchSize.ascent;
  1381. }
  1382. //////////////////////////////////////////////////////////////////////////////
  1383. // 1. Check the common situations where stretching is not actually needed
  1384. //////////////////////////////////////////////////////////////////////////////
  1385. // quick return if there is nothing special about this char
  1386. if ((aStretchDirection != direction &&
  1387. aStretchDirection != NS_STRETCH_DIRECTION_DEFAULT) ||
  1388. (aStretchHint & ~NS_STRETCH_MAXWIDTH) == NS_STRETCH_NONE) {
  1389. mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
  1390. return NS_OK;
  1391. }
  1392. // if no specified direction, attempt to stretch in our preferred direction
  1393. if (aStretchDirection == NS_STRETCH_DIRECTION_DEFAULT) {
  1394. aStretchDirection = direction;
  1395. }
  1396. // see if this is a particular largeop or largeopOnly request
  1397. bool largeop = (NS_STRETCH_LARGEOP & aStretchHint) != 0;
  1398. bool stretchy = (NS_STRETCH_VARIABLE_MASK & aStretchHint) != 0;
  1399. bool largeopOnly = largeop && !stretchy;
  1400. bool isVertical = (direction == NS_STRETCH_DIRECTION_VERTICAL);
  1401. nscoord targetSize =
  1402. isVertical ? aContainerSize.ascent + aContainerSize.descent
  1403. : aContainerSize.rightBearing - aContainerSize.leftBearing;
  1404. if (maxWidth) {
  1405. // See if it is only necessary to consider glyphs up to some maximum size.
  1406. // Set the current height to the maximum size, and set aStretchHint to
  1407. // NS_STRETCH_SMALLER if the size is variable, so that only smaller sizes
  1408. // are considered. targetSize from GetMaxWidth() is 0.
  1409. if (stretchy) {
  1410. // variable size stretch - consider all sizes < maxsize
  1411. aStretchHint =
  1412. (aStretchHint & ~NS_STRETCH_VARIABLE_MASK) | NS_STRETCH_SMALLER;
  1413. }
  1414. // Use NS_MATHML_DELIMITER_FACTOR to allow some slightly larger glyphs as
  1415. // maxsize is not enforced exactly.
  1416. if (aMaxSize == NS_MATHML_OPERATOR_SIZE_INFINITY) {
  1417. aDesiredStretchSize.ascent = nscoord_MAX;
  1418. aDesiredStretchSize.descent = 0;
  1419. }
  1420. else {
  1421. nscoord height = aDesiredStretchSize.ascent + aDesiredStretchSize.descent;
  1422. if (height == 0) {
  1423. if (aMaxSizeIsAbsolute) {
  1424. aDesiredStretchSize.ascent =
  1425. NSToCoordRound(aMaxSize / NS_MATHML_DELIMITER_FACTOR);
  1426. aDesiredStretchSize.descent = 0;
  1427. }
  1428. // else: leave height as 0
  1429. }
  1430. else {
  1431. float scale = aMaxSizeIsAbsolute ? aMaxSize / height : aMaxSize;
  1432. scale /= NS_MATHML_DELIMITER_FACTOR;
  1433. aDesiredStretchSize.ascent =
  1434. NSToCoordRound(scale * aDesiredStretchSize.ascent);
  1435. aDesiredStretchSize.descent =
  1436. NSToCoordRound(scale * aDesiredStretchSize.descent);
  1437. }
  1438. }
  1439. }
  1440. nsBoundingMetrics initialSize = aDesiredStretchSize;
  1441. nscoord charSize =
  1442. isVertical ? initialSize.ascent + initialSize.descent
  1443. : initialSize.rightBearing - initialSize.leftBearing;
  1444. bool done = false;
  1445. if (!maxWidth && !largeop) {
  1446. // Doing Stretch() not GetMaxWidth(),
  1447. // and not a largeop in display mode; we're done if size fits
  1448. if ((targetSize <= 0) ||
  1449. ((isVertical && charSize >= targetSize) ||
  1450. IsSizeOK(charSize, targetSize, aStretchHint)))
  1451. done = true;
  1452. }
  1453. //////////////////////////////////////////////////////////////////////////////
  1454. // 2/3. Search for a glyph or set of part glyphs of appropriate size
  1455. //////////////////////////////////////////////////////////////////////////////
  1456. bool glyphFound = false;
  1457. if (!done) { // normal case
  1458. // Use the css font-family but add preferred fallback fonts.
  1459. font = mStyleContext->StyleFont()->mFont;
  1460. NormalizeDefaultFont(font, aFontSizeInflation);
  1461. // really shouldn't be doing things this way but for now
  1462. // insert fallbacks into the list
  1463. AutoTArray<nsString, 16> mathFallbacks;
  1464. gfxFontUtils::GetPrefsFontList("font.name.serif.x-math", mathFallbacks);
  1465. gfxFontUtils::AppendPrefsFontList("font.name-list.serif.x-math",
  1466. mathFallbacks);
  1467. InsertMathFallbacks(font.fontlist, mathFallbacks);
  1468. #ifdef NOISY_SEARCH
  1469. nsAutoString fontlistStr;
  1470. font.fontlist.ToString(fontlistStr, false, true);
  1471. printf("Searching in "%s" for a glyph of appropriate size for: 0x%04X:%c\n",
  1472. NS_ConvertUTF16toUTF8(fontlistStr).get(), mData[0], mData[0]&0x00FF);
  1473. #endif
  1474. StretchEnumContext enumData(this, aPresContext, aDrawTarget,
  1475. aFontSizeInflation,
  1476. aStretchDirection, targetSize, aStretchHint,
  1477. aDesiredStretchSize, font.fontlist, glyphFound);
  1478. enumData.mTryParts = !largeopOnly;
  1479. const nsTArray<FontFamilyName>& fontlist = font.fontlist.GetFontlist();
  1480. uint32_t i, num = fontlist.Length();
  1481. bool next = true;
  1482. for (i = 0; i < num && next; i++) {
  1483. const FontFamilyName& name = fontlist[i];
  1484. next = StretchEnumContext::EnumCallback(name, name.IsGeneric(), &enumData);
  1485. }
  1486. }
  1487. if (!maxWidth) {
  1488. // Now, we know how we are going to draw the char. Update the member
  1489. // variables accordingly.
  1490. mUnscaledAscent = aDesiredStretchSize.ascent;
  1491. }
  1492. if (glyphFound) {
  1493. return NS_OK;
  1494. }
  1495. // We did not find a size variant or a glyph assembly to stretch this
  1496. // operator. Verify whether a font with an OpenType MATH table is available
  1497. // and record missing math script otherwise.
  1498. gfxMissingFontRecorder* MFR = aPresContext->MissingFontRecorder();
  1499. if (MFR && !fm->GetThebesFontGroup()->GetFirstMathFont()) {
  1500. MFR->RecordScript(unicode::Script::MATHEMATICAL_NOTATION);
  1501. }
  1502. // If the scale_stretchy_operators option is disabled, we are done.
  1503. if (!Preferences::GetBool("mathml.scale_stretchy_operators.enabled", true)) {
  1504. return NS_OK;
  1505. }
  1506. // stretchy character
  1507. if (stretchy) {
  1508. if (isVertical) {
  1509. float scale =
  1510. std::min(kMaxScaleFactor, float(aContainerSize.ascent + aContainerSize.descent) /
  1511. (aDesiredStretchSize.ascent + aDesiredStretchSize.descent));
  1512. if (!largeop || scale > 1.0) {
  1513. // make the character match the desired height.
  1514. if (!maxWidth) {
  1515. mScaleY *= scale;
  1516. }
  1517. aDesiredStretchSize.ascent *= scale;
  1518. aDesiredStretchSize.descent *= scale;
  1519. }
  1520. } else {
  1521. float scale =
  1522. std::min(kMaxScaleFactor, float(aContainerSize.rightBearing - aContainerSize.leftBearing) /
  1523. (aDesiredStretchSize.rightBearing - aDesiredStretchSize.leftBearing));
  1524. if (!largeop || scale > 1.0) {
  1525. // make the character match the desired width.
  1526. if (!maxWidth) {
  1527. mScaleX *= scale;
  1528. }
  1529. aDesiredStretchSize.leftBearing *= scale;
  1530. aDesiredStretchSize.rightBearing *= scale;
  1531. aDesiredStretchSize.width *= scale;
  1532. }
  1533. }
  1534. }
  1535. // We do not have a char variant for this largeop in display mode, so we
  1536. // apply a scale transform to the base char.
  1537. if (largeop) {
  1538. float scale;
  1539. float largeopFactor = kLargeOpFactor;
  1540. // increase the width if it is not largeopFactor times larger
  1541. // than the initial one.
  1542. if ((aDesiredStretchSize.rightBearing - aDesiredStretchSize.leftBearing) <
  1543. largeopFactor * (initialSize.rightBearing - initialSize.leftBearing)) {
  1544. scale = (largeopFactor *
  1545. (initialSize.rightBearing - initialSize.leftBearing)) /
  1546. (aDesiredStretchSize.rightBearing - aDesiredStretchSize.leftBearing);
  1547. if (!maxWidth) {
  1548. mScaleX *= scale;
  1549. }
  1550. aDesiredStretchSize.leftBearing *= scale;
  1551. aDesiredStretchSize.rightBearing *= scale;
  1552. aDesiredStretchSize.width *= scale;
  1553. }
  1554. // increase the height if it is not largeopFactor times larger
  1555. // than the initial one.
  1556. if (NS_STRETCH_INTEGRAL & aStretchHint) {
  1557. // integrals are drawn taller
  1558. largeopFactor = kIntegralFactor;
  1559. }
  1560. if ((aDesiredStretchSize.ascent + aDesiredStretchSize.descent) <
  1561. largeopFactor * (initialSize.ascent + initialSize.descent)) {
  1562. scale = (largeopFactor *
  1563. (initialSize.ascent + initialSize.descent)) /
  1564. (aDesiredStretchSize.ascent + aDesiredStretchSize.descent);
  1565. if (!maxWidth) {
  1566. mScaleY *= scale;
  1567. }
  1568. aDesiredStretchSize.ascent *= scale;
  1569. aDesiredStretchSize.descent *= scale;
  1570. }
  1571. }
  1572. return NS_OK;
  1573. }
  1574. nsresult
  1575. nsMathMLChar::Stretch(nsPresContext* aPresContext,
  1576. DrawTarget* aDrawTarget,
  1577. float aFontSizeInflation,
  1578. nsStretchDirection aStretchDirection,
  1579. const nsBoundingMetrics& aContainerSize,
  1580. nsBoundingMetrics& aDesiredStretchSize,
  1581. uint32_t aStretchHint,
  1582. bool aRTL)
  1583. {
  1584. NS_ASSERTION(!(aStretchHint &
  1585. ~(NS_STRETCH_VARIABLE_MASK | NS_STRETCH_LARGEOP |
  1586. NS_STRETCH_INTEGRAL)),
  1587. "Unexpected stretch flags");
  1588. mDraw = DRAW_NORMAL;
  1589. mMirrored = aRTL && nsMathMLOperators::IsMirrorableOperator(mData);
  1590. mScaleY = mScaleX = 1.0;
  1591. mDirection = aStretchDirection;
  1592. nsresult rv =
  1593. StretchInternal(aPresContext, aDrawTarget, aFontSizeInflation, mDirection,
  1594. aContainerSize, aDesiredStretchSize, aStretchHint);
  1595. // Record the metrics
  1596. mBoundingMetrics = aDesiredStretchSize;
  1597. return rv;
  1598. }
  1599. // What happens here is that the StretchInternal algorithm is used but
  1600. // modified by passing the NS_STRETCH_MAXWIDTH stretch hint. That causes
  1601. // StretchInternal to return horizontal bounding metrics that are the maximum
  1602. // that might be returned from a Stretch.
  1603. //
  1604. // In order to avoid considering widths of some characters in fonts that will
  1605. // not be used for any stretch size, StretchInternal sets the initial height
  1606. // to infinity and looks for any characters smaller than this height. When a
  1607. // character built from parts is considered, (it will be used by Stretch for
  1608. // any characters greater than its minimum size, so) the height is set to its
  1609. // minimum size, so that only widths of smaller subsequent characters are
  1610. // considered.
  1611. nscoord
  1612. nsMathMLChar::GetMaxWidth(nsPresContext* aPresContext, DrawTarget* aDrawTarget,
  1613. float aFontSizeInflation, uint32_t aStretchHint)
  1614. {
  1615. nsBoundingMetrics bm;
  1616. nsStretchDirection direction = NS_STRETCH_DIRECTION_VERTICAL;
  1617. const nsBoundingMetrics container; // zero target size
  1618. StretchInternal(aPresContext, aDrawTarget, aFontSizeInflation, direction,
  1619. container, bm, aStretchHint | NS_STRETCH_MAXWIDTH);
  1620. return std::max(bm.width, bm.rightBearing) - std::min(0, bm.leftBearing);
  1621. }
  1622. class nsDisplayMathMLSelectionRect : public nsDisplayItem {
  1623. public:
  1624. nsDisplayMathMLSelectionRect(nsDisplayListBuilder* aBuilder,
  1625. nsIFrame* aFrame, const nsRect& aRect)
  1626. : nsDisplayItem(aBuilder, aFrame), mRect(aRect) {
  1627. MOZ_COUNT_CTOR(nsDisplayMathMLSelectionRect);
  1628. }
  1629. #ifdef NS_BUILD_REFCNT_LOGGING
  1630. virtual ~nsDisplayMathMLSelectionRect() {
  1631. MOZ_COUNT_DTOR(nsDisplayMathMLSelectionRect);
  1632. }
  1633. #endif
  1634. virtual void Paint(nsDisplayListBuilder* aBuilder,
  1635. nsRenderingContext* aCtx) override;
  1636. NS_DISPLAY_DECL_NAME("MathMLSelectionRect", TYPE_MATHML_SELECTION_RECT)
  1637. private:
  1638. nsRect mRect;
  1639. };
  1640. void nsDisplayMathMLSelectionRect::Paint(nsDisplayListBuilder* aBuilder,
  1641. nsRenderingContext* aCtx)
  1642. {
  1643. DrawTarget* drawTarget = aCtx->GetDrawTarget();
  1644. Rect rect = NSRectToSnappedRect(mRect + ToReferenceFrame(),
  1645. mFrame->PresContext()->AppUnitsPerDevPixel(),
  1646. *drawTarget);
  1647. // get color to use for selection from the look&feel object
  1648. nscolor bgColor =
  1649. LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectBackground,
  1650. NS_RGB(0, 0, 0));
  1651. drawTarget->FillRect(rect, ColorPattern(ToDeviceColor(bgColor)));
  1652. }
  1653. class nsDisplayMathMLCharForeground : public nsDisplayItem {
  1654. public:
  1655. nsDisplayMathMLCharForeground(nsDisplayListBuilder* aBuilder,
  1656. nsIFrame* aFrame, nsMathMLChar* aChar,
  1657. uint32_t aIndex, bool aIsSelected)
  1658. : nsDisplayItem(aBuilder, aFrame), mChar(aChar),
  1659. mIndex(aIndex), mIsSelected(aIsSelected) {
  1660. MOZ_COUNT_CTOR(nsDisplayMathMLCharForeground);
  1661. }
  1662. #ifdef NS_BUILD_REFCNT_LOGGING
  1663. virtual ~nsDisplayMathMLCharForeground() {
  1664. MOZ_COUNT_DTOR(nsDisplayMathMLCharForeground);
  1665. }
  1666. #endif
  1667. virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) override {
  1668. *aSnap = false;
  1669. nsRect rect;
  1670. mChar->GetRect(rect);
  1671. nsPoint offset = ToReferenceFrame() + rect.TopLeft();
  1672. nsBoundingMetrics bm;
  1673. mChar->GetBoundingMetrics(bm);
  1674. nsRect temp(offset.x + bm.leftBearing, offset.y,
  1675. bm.rightBearing - bm.leftBearing, bm.ascent + bm.descent);
  1676. // Bug 748220
  1677. temp.Inflate(mFrame->PresContext()->AppUnitsPerDevPixel());
  1678. return temp;
  1679. }
  1680. virtual void Paint(nsDisplayListBuilder* aBuilder,
  1681. nsRenderingContext* aCtx) override
  1682. {
  1683. mChar->PaintForeground(mFrame->PresContext(), *aCtx,
  1684. ToReferenceFrame(), mIsSelected);
  1685. }
  1686. NS_DISPLAY_DECL_NAME("MathMLCharForeground", TYPE_MATHML_CHAR_FOREGROUND)
  1687. virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) override
  1688. {
  1689. bool snap;
  1690. return GetBounds(aBuilder, &snap);
  1691. }
  1692. virtual uint32_t GetPerFrameKey() override {
  1693. return (mIndex << nsDisplayItem::TYPE_BITS)
  1694. | nsDisplayItem::GetPerFrameKey();
  1695. }
  1696. private:
  1697. nsMathMLChar* mChar;
  1698. uint32_t mIndex;
  1699. bool mIsSelected;
  1700. };
  1701. #ifdef DEBUG
  1702. class nsDisplayMathMLCharDebug : public nsDisplayItem {
  1703. public:
  1704. nsDisplayMathMLCharDebug(nsDisplayListBuilder* aBuilder,
  1705. nsIFrame* aFrame, const nsRect& aRect)
  1706. : nsDisplayItem(aBuilder, aFrame), mRect(aRect) {
  1707. MOZ_COUNT_CTOR(nsDisplayMathMLCharDebug);
  1708. }
  1709. #ifdef NS_BUILD_REFCNT_LOGGING
  1710. virtual ~nsDisplayMathMLCharDebug() {
  1711. MOZ_COUNT_DTOR(nsDisplayMathMLCharDebug);
  1712. }
  1713. #endif
  1714. virtual void Paint(nsDisplayListBuilder* aBuilder,
  1715. nsRenderingContext* aCtx) override;
  1716. NS_DISPLAY_DECL_NAME("MathMLCharDebug", TYPE_MATHML_CHAR_DEBUG)
  1717. private:
  1718. nsRect mRect;
  1719. };
  1720. void nsDisplayMathMLCharDebug::Paint(nsDisplayListBuilder* aBuilder,
  1721. nsRenderingContext* aCtx)
  1722. {
  1723. // for visual debug
  1724. Sides skipSides;
  1725. nsPresContext* presContext = mFrame->PresContext();
  1726. nsStyleContext* styleContext = mFrame->StyleContext();
  1727. nsRect rect = mRect + ToReferenceFrame();
  1728. PaintBorderFlags flags = aBuilder->ShouldSyncDecodeImages()
  1729. ? PaintBorderFlags::SYNC_DECODE_IMAGES
  1730. : PaintBorderFlags();
  1731. // Since this is used only for debugging, we don't need to worry about
  1732. // tracking the DrawResult.
  1733. Unused <<
  1734. nsCSSRendering::PaintBorder(presContext, *aCtx, mFrame, mVisibleRect,
  1735. rect, styleContext, flags, skipSides);
  1736. nsCSSRendering::PaintOutline(presContext, *aCtx, mFrame,
  1737. mVisibleRect, rect, styleContext);
  1738. }
  1739. #endif
  1740. void
  1741. nsMathMLChar::Display(nsDisplayListBuilder* aBuilder,
  1742. nsIFrame* aForFrame,
  1743. const nsDisplayListSet& aLists,
  1744. uint32_t aIndex,
  1745. const nsRect* aSelectedRect)
  1746. {
  1747. nsStyleContext* parentContext = mStyleContext->GetParent();
  1748. nsStyleContext* styleContext = mStyleContext;
  1749. if (mDraw == DRAW_NORMAL) {
  1750. // normal drawing if there is nothing special about this char
  1751. // Set default context to the parent context
  1752. styleContext = parentContext;
  1753. }
  1754. if (!styleContext->StyleVisibility()->IsVisible())
  1755. return;
  1756. // if the leaf style context that we use for stretchy chars has a background
  1757. // color we use it -- this feature is mostly used for testing and debugging
  1758. // purposes. Normally, users will set the background on the container frame.
  1759. // paint the selection background -- beware MathML frames overlap a lot
  1760. if (aSelectedRect && !aSelectedRect->IsEmpty()) {
  1761. aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
  1762. nsDisplayMathMLSelectionRect(aBuilder, aForFrame, *aSelectedRect));
  1763. }
  1764. else if (mRect.width && mRect.height) {
  1765. const nsStyleBackground* backg = styleContext->StyleBackground();
  1766. if (styleContext != parentContext &&
  1767. NS_GET_A(backg->mBackgroundColor) > 0) {
  1768. nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
  1769. aBuilder, aForFrame, mRect + aBuilder->ToReferenceFrame(aForFrame),
  1770. aLists.BorderBackground(),
  1771. /* aAllowWillPaintBorderOptimization */ true, styleContext);
  1772. }
  1773. //else
  1774. // our container frame will take care of painting its background
  1775. #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
  1776. // for visual debug
  1777. aLists.BorderBackground()->AppendToTop(new (aBuilder)
  1778. nsDisplayMathMLCharDebug(aBuilder, aForFrame, mRect));
  1779. #endif
  1780. }
  1781. aLists.Content()->AppendNewToTop(new (aBuilder)
  1782. nsDisplayMathMLCharForeground(aBuilder, aForFrame, this,
  1783. aIndex,
  1784. aSelectedRect &&
  1785. !aSelectedRect->IsEmpty()));
  1786. }
  1787. void
  1788. nsMathMLChar::ApplyTransforms(gfxContext* aThebesContext,
  1789. int32_t aAppUnitsPerGfxUnit,
  1790. nsRect &r)
  1791. {
  1792. // apply the transforms
  1793. if (mMirrored) {
  1794. nsPoint pt = r.TopRight();
  1795. gfxPoint devPixelOffset(NSAppUnitsToFloatPixels(pt.x, aAppUnitsPerGfxUnit),
  1796. NSAppUnitsToFloatPixels(pt.y, aAppUnitsPerGfxUnit));
  1797. aThebesContext->SetMatrix(
  1798. aThebesContext->CurrentMatrix().Translate(devPixelOffset).
  1799. Scale(-mScaleX, mScaleY));
  1800. } else {
  1801. nsPoint pt = r.TopLeft();
  1802. gfxPoint devPixelOffset(NSAppUnitsToFloatPixels(pt.x, aAppUnitsPerGfxUnit),
  1803. NSAppUnitsToFloatPixels(pt.y, aAppUnitsPerGfxUnit));
  1804. aThebesContext->SetMatrix(
  1805. aThebesContext->CurrentMatrix().Translate(devPixelOffset).
  1806. Scale(mScaleX, mScaleY));
  1807. }
  1808. // update the bounding rectangle.
  1809. r.x = r.y = 0;
  1810. r.width /= mScaleX;
  1811. r.height /= mScaleY;
  1812. }
  1813. void
  1814. nsMathMLChar::PaintForeground(nsPresContext* aPresContext,
  1815. nsRenderingContext& aRenderingContext,
  1816. nsPoint aPt,
  1817. bool aIsSelected)
  1818. {
  1819. nsStyleContext* parentContext = mStyleContext->GetParent();
  1820. nsStyleContext* styleContext = mStyleContext;
  1821. if (mDraw == DRAW_NORMAL) {
  1822. // normal drawing if there is nothing special about this char
  1823. // Set default context to the parent context
  1824. styleContext = parentContext;
  1825. }
  1826. RefPtr<gfxContext> thebesContext = aRenderingContext.ThebesContext();
  1827. // Set color ...
  1828. nscolor fgColor = styleContext->
  1829. GetVisitedDependentColor(eCSSProperty__webkit_text_fill_color);
  1830. if (aIsSelected) {
  1831. // get color to use for selection from the look&feel object
  1832. fgColor = LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectForeground,
  1833. fgColor);
  1834. }
  1835. thebesContext->SetColor(Color::FromABGR(fgColor));
  1836. thebesContext->Save();
  1837. nsRect r = mRect + aPt;
  1838. ApplyTransforms(thebesContext, aPresContext->AppUnitsPerDevPixel(), r);
  1839. switch(mDraw)
  1840. {
  1841. case DRAW_NORMAL:
  1842. case DRAW_VARIANT:
  1843. // draw a single glyph (base size or size variant)
  1844. // XXXfredw verify if mGlyphs[0] is non-null to workaround bug 973322.
  1845. if (mGlyphs[0]) {
  1846. mGlyphs[0]->Draw(Range(mGlyphs[0].get()), gfxPoint(0.0, mUnscaledAscent),
  1847. gfxTextRun::DrawParams(thebesContext));
  1848. }
  1849. break;
  1850. case DRAW_PARTS: {
  1851. // paint by parts
  1852. if (NS_STRETCH_DIRECTION_VERTICAL == mDirection)
  1853. PaintVertically(aPresContext, thebesContext, r, fgColor);
  1854. else if (NS_STRETCH_DIRECTION_HORIZONTAL == mDirection)
  1855. PaintHorizontally(aPresContext, thebesContext, r, fgColor);
  1856. break;
  1857. }
  1858. default:
  1859. NS_NOTREACHED("Unknown drawing method");
  1860. break;
  1861. }
  1862. thebesContext->Restore();
  1863. }
  1864. /* =============================================================================
  1865. Helper routines that actually do the job of painting the char by parts
  1866. */
  1867. class AutoPushClipRect {
  1868. gfxContext* mThebesContext;
  1869. public:
  1870. AutoPushClipRect(gfxContext* aThebesContext, int32_t aAppUnitsPerGfxUnit,
  1871. const nsRect& aRect)
  1872. : mThebesContext(aThebesContext) {
  1873. mThebesContext->Save();
  1874. mThebesContext->NewPath();
  1875. gfxRect clip = nsLayoutUtils::RectToGfxRect(aRect, aAppUnitsPerGfxUnit);
  1876. mThebesContext->SnappedRectangle(clip);
  1877. mThebesContext->Clip();
  1878. }
  1879. ~AutoPushClipRect() {
  1880. mThebesContext->Restore();
  1881. }
  1882. };
  1883. static nsPoint
  1884. SnapToDevPixels(const gfxContext* aThebesContext, int32_t aAppUnitsPerGfxUnit,
  1885. const nsPoint& aPt)
  1886. {
  1887. gfxPoint pt(NSAppUnitsToFloatPixels(aPt.x, aAppUnitsPerGfxUnit),
  1888. NSAppUnitsToFloatPixels(aPt.y, aAppUnitsPerGfxUnit));
  1889. pt = aThebesContext->UserToDevice(pt);
  1890. pt.Round();
  1891. pt = aThebesContext->DeviceToUser(pt);
  1892. return nsPoint(NSFloatPixelsToAppUnits(pt.x, aAppUnitsPerGfxUnit),
  1893. NSFloatPixelsToAppUnits(pt.y, aAppUnitsPerGfxUnit));
  1894. }
  1895. static void
  1896. PaintRule(DrawTarget& aDrawTarget,
  1897. int32_t aAppUnitsPerGfxUnit,
  1898. nsRect& aRect,
  1899. nscolor aColor)
  1900. {
  1901. Rect rect = NSRectToSnappedRect(aRect, aAppUnitsPerGfxUnit, aDrawTarget);
  1902. ColorPattern color(ToDeviceColor(aColor));
  1903. aDrawTarget.FillRect(rect, color);
  1904. }
  1905. // paint a stretchy char by assembling glyphs vertically
  1906. nsresult
  1907. nsMathMLChar::PaintVertically(nsPresContext* aPresContext,
  1908. gfxContext* aThebesContext,
  1909. nsRect& aRect,
  1910. nscolor aColor)
  1911. {
  1912. DrawTarget& aDrawTarget = *aThebesContext->GetDrawTarget();
  1913. // Get the device pixel size in the vertical direction.
  1914. // (This makes no effort to optimize for non-translation transformations.)
  1915. nscoord oneDevPixel = aPresContext->AppUnitsPerDevPixel();
  1916. // get metrics data to be re-used later
  1917. int32_t i = 0;
  1918. nscoord dx = aRect.x;
  1919. nscoord offset[3], start[3], end[3];
  1920. for (i = 0; i <= 2; ++i) {
  1921. const nsBoundingMetrics& bm = mBmData[i];
  1922. nscoord dy;
  1923. if (0 == i) { // top
  1924. dy = aRect.y + bm.ascent;
  1925. }
  1926. else if (2 == i) { // bottom
  1927. dy = aRect.y + aRect.height - bm.descent;
  1928. }
  1929. else { // middle
  1930. dy = aRect.y + bm.ascent + (aRect.height - (bm.ascent + bm.descent))/2;
  1931. }
  1932. // _cairo_scaled_font_show_glyphs snaps origins to device pixels.
  1933. // Do this now so that we can get the other dimensions right.
  1934. // (This may not achieve much with non-rectangular transformations.)
  1935. dy = SnapToDevPixels(aThebesContext, oneDevPixel, nsPoint(dx, dy)).y;
  1936. // abcissa passed to Draw
  1937. offset[i] = dy;
  1938. // _cairo_scaled_font_glyph_device_extents rounds outwards to the nearest
  1939. // pixel, so the bm values can include 1 row of faint pixels on each edge.
  1940. // Don't rely on this pixel as it can look like a gap.
  1941. if (bm.ascent + bm.descent >= 2 * oneDevPixel) {
  1942. start[i] = dy - bm.ascent + oneDevPixel; // top join
  1943. end[i] = dy + bm.descent - oneDevPixel; // bottom join
  1944. } else {
  1945. // To avoid overlaps, we don't add one pixel on each side when the part
  1946. // is too small.
  1947. start[i] = dy - bm.ascent; // top join
  1948. end[i] = dy + bm.descent; // bottom join
  1949. }
  1950. }
  1951. // If there are overlaps, then join at the mid point
  1952. for (i = 0; i < 2; ++i) {
  1953. if (end[i] > start[i+1]) {
  1954. end[i] = (end[i] + start[i+1]) / 2;
  1955. start[i+1] = end[i];
  1956. }
  1957. }
  1958. nsRect unionRect = aRect;
  1959. unionRect.x += mBoundingMetrics.leftBearing;
  1960. unionRect.width =
  1961. mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing;
  1962. unionRect.Inflate(oneDevPixel, oneDevPixel);
  1963. gfxTextRun::DrawParams params(aThebesContext);
  1964. /////////////////////////////////////
  1965. // draw top, middle, bottom
  1966. for (i = 0; i <= 2; ++i) {
  1967. // glue can be null
  1968. if (mGlyphs[i]) {
  1969. nscoord dy = offset[i];
  1970. // Draw a glyph in a clipped area so that we don't have hairy chars
  1971. // pending outside
  1972. nsRect clipRect = unionRect;
  1973. // Clip at the join to get a solid edge (without overlap or gap), when
  1974. // this won't change the glyph too much. If the glyph is too small to
  1975. // clip then we'll overlap rather than have a gap.
  1976. nscoord height = mBmData[i].ascent + mBmData[i].descent;
  1977. if (height * (1.0 - NS_MATHML_DELIMITER_FACTOR) > oneDevPixel) {
  1978. if (0 == i) { // top
  1979. clipRect.height = end[i] - clipRect.y;
  1980. }
  1981. else if (2 == i) { // bottom
  1982. clipRect.height -= start[i] - clipRect.y;
  1983. clipRect.y = start[i];
  1984. }
  1985. else { // middle
  1986. clipRect.y = start[i];
  1987. clipRect.height = end[i] - start[i];
  1988. }
  1989. }
  1990. if (!clipRect.IsEmpty()) {
  1991. AutoPushClipRect clip(aThebesContext, oneDevPixel, clipRect);
  1992. mGlyphs[i]->Draw(Range(mGlyphs[i].get()), gfxPoint(dx, dy), params);
  1993. }
  1994. }
  1995. }
  1996. ///////////////
  1997. // fill the gap between top and middle, and between middle and bottom.
  1998. if (!mGlyphs[3]) { // null glue : draw a rule
  1999. // figure out the dimensions of the rule to be drawn :
  2000. // set lbearing to rightmost lbearing among the two current successive
  2001. // parts.
  2002. // set rbearing to leftmost rbearing among the two current successive parts.
  2003. // this not only satisfies the convention used for over/underbraces
  2004. // in TeX, but also takes care of broken fonts like the stretchy integral
  2005. // in Symbol for small font sizes in unix.
  2006. nscoord lbearing, rbearing;
  2007. int32_t first = 0, last = 1;
  2008. while (last <= 2) {
  2009. if (mGlyphs[last]) {
  2010. lbearing = mBmData[last].leftBearing;
  2011. rbearing = mBmData[last].rightBearing;
  2012. if (mGlyphs[first]) {
  2013. if (lbearing < mBmData[first].leftBearing)
  2014. lbearing = mBmData[first].leftBearing;
  2015. if (rbearing > mBmData[first].rightBearing)
  2016. rbearing = mBmData[first].rightBearing;
  2017. }
  2018. }
  2019. else if (mGlyphs[first]) {
  2020. lbearing = mBmData[first].leftBearing;
  2021. rbearing = mBmData[first].rightBearing;
  2022. }
  2023. else {
  2024. NS_ERROR("Cannot stretch - All parts missing");
  2025. return NS_ERROR_UNEXPECTED;
  2026. }
  2027. // paint the rule between the parts
  2028. nsRect rule(aRect.x + lbearing, end[first],
  2029. rbearing - lbearing, start[last] - end[first]);
  2030. PaintRule(aDrawTarget, oneDevPixel, rule, aColor);
  2031. first = last;
  2032. last++;
  2033. }
  2034. }
  2035. else if (mBmData[3].ascent + mBmData[3].descent > 0) {
  2036. // glue is present
  2037. nsBoundingMetrics& bm = mBmData[3];
  2038. // Ensure the stride for the glue is not reduced to less than one pixel
  2039. if (bm.ascent + bm.descent >= 3 * oneDevPixel) {
  2040. // To protect against gaps, pretend the glue is smaller than it is,
  2041. // in order to trim off ends and thus get a solid edge for the join.
  2042. bm.ascent -= oneDevPixel;
  2043. bm.descent -= oneDevPixel;
  2044. }
  2045. nsRect clipRect = unionRect;
  2046. for (i = 0; i < 2; ++i) {
  2047. // Make sure not to draw outside the character
  2048. nscoord dy = std::max(end[i], aRect.y);
  2049. nscoord fillEnd = std::min(start[i+1], aRect.YMost());
  2050. while (dy < fillEnd) {
  2051. clipRect.y = dy;
  2052. clipRect.height = std::min(bm.ascent + bm.descent, fillEnd - dy);
  2053. AutoPushClipRect clip(aThebesContext, oneDevPixel, clipRect);
  2054. dy += bm.ascent;
  2055. mGlyphs[3]->Draw(Range(mGlyphs[3].get()), gfxPoint(dx, dy), params);
  2056. dy += bm.descent;
  2057. }
  2058. }
  2059. }
  2060. #ifdef DEBUG
  2061. else {
  2062. for (i = 0; i < 2; ++i) {
  2063. NS_ASSERTION(end[i] >= start[i+1],
  2064. "gap between parts with missing glue glyph");
  2065. }
  2066. }
  2067. #endif
  2068. return NS_OK;
  2069. }
  2070. // paint a stretchy char by assembling glyphs horizontally
  2071. nsresult
  2072. nsMathMLChar::PaintHorizontally(nsPresContext* aPresContext,
  2073. gfxContext* aThebesContext,
  2074. nsRect& aRect,
  2075. nscolor aColor)
  2076. {
  2077. DrawTarget& aDrawTarget = *aThebesContext->GetDrawTarget();
  2078. // Get the device pixel size in the horizontal direction.
  2079. // (This makes no effort to optimize for non-translation transformations.)
  2080. nscoord oneDevPixel = aPresContext->AppUnitsPerDevPixel();
  2081. // get metrics data to be re-used later
  2082. int32_t i = 0;
  2083. nscoord dy = aRect.y + mBoundingMetrics.ascent;
  2084. nscoord offset[3], start[3], end[3];
  2085. for (i = 0; i <= 2; ++i) {
  2086. const nsBoundingMetrics& bm = mBmData[i];
  2087. nscoord dx;
  2088. if (0 == i) { // left
  2089. dx = aRect.x - bm.leftBearing;
  2090. }
  2091. else if (2 == i) { // right
  2092. dx = aRect.x + aRect.width - bm.rightBearing;
  2093. }
  2094. else { // middle
  2095. dx = aRect.x + (aRect.width - bm.width)/2;
  2096. }
  2097. // _cairo_scaled_font_show_glyphs snaps origins to device pixels.
  2098. // Do this now so that we can get the other dimensions right.
  2099. // (This may not achieve much with non-rectangular transformations.)
  2100. dx = SnapToDevPixels(aThebesContext, oneDevPixel, nsPoint(dx, dy)).x;
  2101. // abcissa passed to Draw
  2102. offset[i] = dx;
  2103. // _cairo_scaled_font_glyph_device_extents rounds outwards to the nearest
  2104. // pixel, so the bm values can include 1 row of faint pixels on each edge.
  2105. // Don't rely on this pixel as it can look like a gap.
  2106. if (bm.rightBearing - bm.leftBearing >= 2 * oneDevPixel) {
  2107. start[i] = dx + bm.leftBearing + oneDevPixel; // left join
  2108. end[i] = dx + bm.rightBearing - oneDevPixel; // right join
  2109. } else {
  2110. // To avoid overlaps, we don't add one pixel on each side when the part
  2111. // is too small.
  2112. start[i] = dx + bm.leftBearing; // left join
  2113. end[i] = dx + bm.rightBearing; // right join
  2114. }
  2115. }
  2116. // If there are overlaps, then join at the mid point
  2117. for (i = 0; i < 2; ++i) {
  2118. if (end[i] > start[i+1]) {
  2119. end[i] = (end[i] + start[i+1]) / 2;
  2120. start[i+1] = end[i];
  2121. }
  2122. }
  2123. nsRect unionRect = aRect;
  2124. unionRect.Inflate(oneDevPixel, oneDevPixel);
  2125. gfxTextRun::DrawParams params(aThebesContext);
  2126. ///////////////////////////
  2127. // draw left, middle, right
  2128. for (i = 0; i <= 2; ++i) {
  2129. // glue can be null
  2130. if (mGlyphs[i]) {
  2131. nscoord dx = offset[i];
  2132. nsRect clipRect = unionRect;
  2133. // Clip at the join to get a solid edge (without overlap or gap), when
  2134. // this won't change the glyph too much. If the glyph is too small to
  2135. // clip then we'll overlap rather than have a gap.
  2136. nscoord width = mBmData[i].rightBearing - mBmData[i].leftBearing;
  2137. if (width * (1.0 - NS_MATHML_DELIMITER_FACTOR) > oneDevPixel) {
  2138. if (0 == i) { // left
  2139. clipRect.width = end[i] - clipRect.x;
  2140. }
  2141. else if (2 == i) { // right
  2142. clipRect.width -= start[i] - clipRect.x;
  2143. clipRect.x = start[i];
  2144. }
  2145. else { // middle
  2146. clipRect.x = start[i];
  2147. clipRect.width = end[i] - start[i];
  2148. }
  2149. }
  2150. if (!clipRect.IsEmpty()) {
  2151. AutoPushClipRect clip(aThebesContext, oneDevPixel, clipRect);
  2152. mGlyphs[i]->Draw(Range(mGlyphs[i].get()), gfxPoint(dx, dy), params);
  2153. }
  2154. }
  2155. }
  2156. ////////////////
  2157. // fill the gap between left and middle, and between middle and right.
  2158. if (!mGlyphs[3]) { // null glue : draw a rule
  2159. // figure out the dimensions of the rule to be drawn :
  2160. // set ascent to lowest ascent among the two current successive parts.
  2161. // set descent to highest descent among the two current successive parts.
  2162. // this satisfies the convention used for over/underbraces, and helps
  2163. // fix broken fonts.
  2164. nscoord ascent, descent;
  2165. int32_t first = 0, last = 1;
  2166. while (last <= 2) {
  2167. if (mGlyphs[last]) {
  2168. ascent = mBmData[last].ascent;
  2169. descent = mBmData[last].descent;
  2170. if (mGlyphs[first]) {
  2171. if (ascent > mBmData[first].ascent)
  2172. ascent = mBmData[first].ascent;
  2173. if (descent > mBmData[first].descent)
  2174. descent = mBmData[first].descent;
  2175. }
  2176. }
  2177. else if (mGlyphs[first]) {
  2178. ascent = mBmData[first].ascent;
  2179. descent = mBmData[first].descent;
  2180. }
  2181. else {
  2182. NS_ERROR("Cannot stretch - All parts missing");
  2183. return NS_ERROR_UNEXPECTED;
  2184. }
  2185. // paint the rule between the parts
  2186. nsRect rule(end[first], dy - ascent,
  2187. start[last] - end[first], ascent + descent);
  2188. PaintRule(aDrawTarget, oneDevPixel, rule, aColor);
  2189. first = last;
  2190. last++;
  2191. }
  2192. }
  2193. else if (mBmData[3].rightBearing - mBmData[3].leftBearing > 0) {
  2194. // glue is present
  2195. nsBoundingMetrics& bm = mBmData[3];
  2196. // Ensure the stride for the glue is not reduced to less than one pixel
  2197. if (bm.rightBearing - bm.leftBearing >= 3 * oneDevPixel) {
  2198. // To protect against gaps, pretend the glue is smaller than it is,
  2199. // in order to trim off ends and thus get a solid edge for the join.
  2200. bm.leftBearing += oneDevPixel;
  2201. bm.rightBearing -= oneDevPixel;
  2202. }
  2203. nsRect clipRect = unionRect;
  2204. for (i = 0; i < 2; ++i) {
  2205. // Make sure not to draw outside the character
  2206. nscoord dx = std::max(end[i], aRect.x);
  2207. nscoord fillEnd = std::min(start[i+1], aRect.XMost());
  2208. while (dx < fillEnd) {
  2209. clipRect.x = dx;
  2210. clipRect.width = std::min(bm.rightBearing - bm.leftBearing, fillEnd - dx);
  2211. AutoPushClipRect clip(aThebesContext, oneDevPixel, clipRect);
  2212. dx -= bm.leftBearing;
  2213. mGlyphs[3]->Draw(Range(mGlyphs[3].get()), gfxPoint(dx, dy), params);
  2214. dx += bm.rightBearing;
  2215. }
  2216. }
  2217. }
  2218. #ifdef DEBUG
  2219. else { // no glue
  2220. for (i = 0; i < 2; ++i) {
  2221. NS_ASSERTION(end[i] >= start[i+1],
  2222. "gap between parts with missing glue glyph");
  2223. }
  2224. }
  2225. #endif
  2226. return NS_OK;
  2227. }