Skip to content

Commit 88ebff7

Browse files
Omikhleiaalerque
authored andcommitted
feat(math): Support MathML mroot element
1 parent e243b2f commit 88ebff7

2 files changed

Lines changed: 87 additions & 26 deletions

File tree

packages/math/base-elements.lua

Lines changed: 84 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1372,23 +1372,48 @@ end
13721372

13731373
function elements.table.output (_) end
13741374

1375+
local function getRadicandMode (mode)
1376+
-- Not too sure if we should do something special/
1377+
return mode
1378+
end
1379+
1380+
local function getDegreeMode (mode)
1381+
-- 2 levels smaller, up to scriptScript evntually.
1382+
-- Not too sure if we should do something else.
1383+
if mode == mathMode.display then
1384+
return mathMode.scriptScript
1385+
elseif mode == mathMode.displayCramped then
1386+
return mathMode.scriptScriptCramped
1387+
elseif mode == mathMode.text or mode == mathMode.script or mode == mathMode.scriptScript then
1388+
return mathMode.scriptScript
1389+
end
1390+
return mathMode.scriptScriptCramped
1391+
end
1392+
13751393
elements.sqrt = pl.class(elements.mbox)
13761394
elements.sqrt._type = "Sqrt"
13771395

13781396
function elements.sqrt:__tostring ()
1379-
return self._type .. "(" .. tostring(self.radicand) .. ")"
1397+
return self._type .. "(" .. tostring(self.radicand) .. (self.degree and ", " .. tostring(self.degree) or "") .. ")"
13801398
end
13811399

1382-
function elements.sqrt:_init (radicand)
1400+
function elements.sqrt:_init (radicand, degree)
13831401
elements.mbox._init(self)
13841402
self.radicand = radicand
1403+
if degree then
1404+
self.degree = degree
1405+
table.insert(self.children, degree)
1406+
end
13851407
table.insert(self.children, radicand)
1386-
self.relX = SILE.types.length(0) -- x position relative to its parent box
1387-
self.relY = SILE.types.length(0) -- y position relative to its parent box
1408+
self.relX = SILE.types.length()
1409+
self.relY = SILE.types.length()
13881410
end
13891411

13901412
function elements.sqrt:styleChildren ()
1391-
self.radicand.mode = self.mode
1413+
self.radicand.mode = getRadicandMode(self.mode)
1414+
if self.degree then
1415+
self.degree.mode = getDegreeMode(self.mode)
1416+
end
13921417
end
13931418

13941419
function elements.sqrt:shape ()
@@ -1404,15 +1429,42 @@ function elements.sqrt:shape ()
14041429
end
14051430
self.extraAscender = constants.radicalExtraAscender * scaleDown
14061431

1407-
-- HACK: More or less ad hoc values, see output method for more details
1408-
self.symbolWidth = SILE.shaper:measureChar("").width
1409-
self.symbolHeight = SILE.types.length("1.1ex"):tonumber() * scaleDown
1410-
1411-
self.width = self.radicand.width + SILE.types.length(self.symbolWidth)
1412-
self.height = self.radicand.height + self.radicalVerticalGap + self.extraAscender
1432+
-- HACK: We draw own own radical sign in the output() method.
1433+
-- Derive dimensions for the radical sign (more or less ad hoc).
1434+
-- Note: In TeX, the radical sign extends a lot below the baseline,
1435+
-- and MathML Core also has a lot of layout text about it.
1436+
-- Not only it doesn't look good, but it's not very clear vs. OpenType.
1437+
local radicalGlyph = SILE.shaper:measureChar("")
1438+
local ratio = (self.radicand.height:tonumber() + self.radicand.depth:tonumber())
1439+
/ (radicalGlyph.height + radicalGlyph.depth)
1440+
local vertAdHocOffset = (ratio > 1 and math.log(ratio) or 0) * self.radicalVerticalGap
1441+
self.symbolHeight = SILE.types.length(radicalGlyph.height) * scaleDown
1442+
self.symbolDepth = (SILE.types.length(radicalGlyph.depth) + vertAdHocOffset) * scaleDown
1443+
self.symbolWidth = (SILE.types.length(radicalGlyph.width) + vertAdHocOffset) * scaleDown
1444+
1445+
-- Adjust the height of the radical sign if the radicand is higher
1446+
self.symbolHeight = self.radicand.height > self.symbolHeight and self.radicand.height or self.symbolHeight
1447+
-- Compute the (max-)height of the short leg of the radical sign
1448+
self.symbolShortHeight = self.symbolHeight * constants.radicalDegreeBottomRaisePercent
1449+
1450+
self.offsetX = SILE.types.length()
1451+
if self.degree then
1452+
-- Position the degree
1453+
self.degree.relY = -constants.radicalDegreeBottomRaisePercent * self.symbolHeight
1454+
-- Adjust the height of the short leg of the radical sign to ensure the degree is not too close
1455+
-- (empirically use radicalExtraAscender)
1456+
self.symbolShortHeight = self.symbolShortHeight - constants.radicalExtraAscender * scaleDown
1457+
-- Compute the width adjustment for the degree
1458+
self.offsetX = self.degree.width
1459+
+ constants.radicalKernBeforeDegree * scaleDown
1460+
+ constants.radicalKernAfterDegree * scaleDown
1461+
end
1462+
-- Position the radicand
1463+
self.radicand.relX = self.symbolWidth + self.offsetX
1464+
-- Compute the dimentions of the whole radical
1465+
self.width = self.radicand.width + self.symbolWidth + self.offsetX
1466+
self.height = self.symbolHeight + self.radicalVerticalGap + self.extraAscender
14131467
self.depth = self.radicand.depth
1414-
self.radicand:shape()
1415-
self.radicand.relX = self.symbolWidth
14161468
end
14171469

14181470
local function _r (number)
@@ -1422,40 +1474,46 @@ local function _r (number)
14221474
end
14231475

14241476
function elements.sqrt:output (x, y, line)
1425-
-- HACK FIXME:
1477+
-- HACK:
14261478
-- OpenType might say we need to assemble the radical sign from parts.
14271479
-- Frankly, it's much easier to just draw it as a graphic :-)
14281480
-- Hence, here we use a PDF graphic operators to draw a nice radical sign.
14291481
-- Some values here are ad hoc, but they look good.
14301482
local h = self.height:tonumber()
14311483
local d = self.depth:tonumber()
1432-
local sw = self.symbolWidth
1433-
local dh = h - self.symbolHeight
1484+
local s0 = scaleWidth(self.offsetX, line):tonumber()
1485+
local sw = scaleWidth(self.symbolWidth, line):tonumber()
1486+
local dsh = h - self.symbolShortHeight:tonumber()
1487+
local dsd = self.symbolDepth:tonumber()
14341488
local symbol = {
14351489
_r(self.radicalRuleThickness),
14361490
"w", -- line width
14371491
2,
14381492
"j", -- round line joins
1439-
_r(sw),
1493+
_r(sw + s0),
14401494
_r(self.extraAscender),
14411495
"m",
1442-
_r(sw * 0.4),
1443-
_r(h + d),
1496+
_r(s0 + sw * 0.90),
1497+
_r(self.extraAscender),
1498+
"l",
1499+
_r(s0 + sw * 0.4),
1500+
_r(h + d + dsd),
14441501
"l",
1445-
_r(sw * 0.15),
1446-
_r(dh),
1502+
_r(s0 + sw * 0.2),
1503+
_r(dsh),
14471504
"l",
1448-
0,
1449-
_r(dh + 0.5),
1505+
s0 + sw * 0.1,
1506+
_r(dsh + 0.5),
14501507
"l",
14511508
"S",
14521509
}
14531510
local svg = table.concat(symbol, " ")
1454-
SILE.outputter:drawSVG(svg, x, y, sw, h, 1)
1511+
local xscaled = scaleWidth(x, line)
1512+
SILE.outputter:drawSVG(svg, xscaled, y, sw, h, 1)
14551513
-- And now we just need to draw the bar over the radicand
14561514
SILE.outputter:drawRule(
1457-
self.symbolWidth + scaleWidth(x, line),
1458-
y.length - scaleWidth(self.radicand.height, line) - self.radicalVerticalGap - self.radicalRuleThickness / 2,
1515+
s0 + self.symbolWidth + xscaled,
1516+
y.length - self.height + self.extraAscender - self.radicalRuleThickness / 2,
14591517
scaleWidth(self.radicand.width, line),
14601518
self.radicalRuleThickness
14611519
)

packages/math/typesetter.lua

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,9 @@ function ConvertMathML (_, content)
134134
local children = convertChildren(content)
135135
-- "The <msqrt> element generates an anonymous <mrow> box called the msqrt base
136136
return b.sqrt(b.stackbox("H", children))
137+
elseif content.command == "mroot" then
138+
local children = convertChildren(content)
139+
return b.sqrt(children[1], children[2])
137140
elseif content.command == "mtable" or content.command == "table" then
138141
local children = convertChildren(content)
139142
return b.table(children, content.options)

0 commit comments

Comments
 (0)