Skip to content

Commit

Permalink
implements \mathbb, \mathrm, \mathit, \mathcal, and \mathfrak
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinbarabash committed May 27, 2015
1 parent 8691486 commit ecc5170
Show file tree
Hide file tree
Showing 15 changed files with 247 additions and 16 deletions.
6 changes: 4 additions & 2 deletions metrics/extract_tfms.py
Expand Up @@ -31,7 +31,8 @@ def main():
'cmsy10.tfm',
'cmti10.tfm',
'msam10.tfm',
'msbm10.tfm'
'msbm10.tfm',
'eufm10.tfm'
]

# Extracted by running `\font\a=<font>` and then `\showthe\skewchar\a` in
Expand All @@ -48,7 +49,8 @@ def main():
'cmsy10': 48,
'cmti10': None,
'msam10': None,
'msbm10': None
'msbm10': None,
'eufm10': None
}

font_name_to_tfm = {}
Expand Down
13 changes: 13 additions & 0 deletions metrics/mapping.pl
Expand Up @@ -256,6 +256,11 @@
"Math-Italic" => [
0x36 => 0x2F # \not
],

"Calligraphic-Regular" => [
[0x30,0x39] => 0x30, # 0-9
[0x41,0x5A] => 0x41, # A-Z
],
};

$map{cmex10} = {
Expand Down Expand Up @@ -944,6 +949,14 @@
],
};

$map{eufm10} = {
"Fraktur-Regular" => [
[0x30,0x39] => 0x30, # 0-9
[0x41,0x5A] => 0x41, # A-Z
[0x61,0x7A] => 0x61, # a-z
],
};

foreach $cmfont (keys %map) {
foreach $mjfont (keys %{$map{$cmfont}}) {
$style = $mjfont; $style =~ s/.*?(-|$)//; $style = "Regular" unless $style;
Expand Down
19 changes: 15 additions & 4 deletions src/Options.js
Expand Up @@ -6,9 +6,9 @@
*/

/**
* This is the main options class. It contains the style, size, and color of the
* current parse level. It also contains the style and size of the parent parse
* level, so size changes can be handled efficiently.
* This is the main options class. It contains the style, size, color, and font
* of the current parse level. It also contains the style and size of the parent
* parse level, so size changes can be handled efficiently.
*
* Each of the `.with*` and `.reset` functions passes its current style and size
* as the parentStyle and parentSize of the new options class, so parent
Expand All @@ -19,6 +19,7 @@ function Options(data) {
this.color = data.color;
this.size = data.size;
this.phantom = data.phantom;
this.font = data.font;

if (data.parentStyle === undefined) {
this.parentStyle = data.style;
Expand All @@ -44,7 +45,8 @@ Options.prototype.extend = function(extension) {
color: this.color,
parentStyle: this.style,
parentSize: this.size,
phantom: this.phantom
phantom: this.phantom,
font: this.font
};

for (var key in extension) {
Expand Down Expand Up @@ -92,6 +94,15 @@ Options.prototype.withPhantom = function() {
});
};

/**
* Create a new options objects with the give font.
*/
Options.prototype.withFont = function(font) {
return this.extend({
font: font
});
};

/**
* Create a new options object with the same style, size, and color. This is
* used so that parent style and size changes are handled correctly.
Expand Down
27 changes: 27 additions & 0 deletions src/buildCommon.js
Expand Up @@ -62,6 +62,30 @@ var mathrm = function(value, mode, color, classes) {
}
};

/**
* Makes a symbol in the blackboard bold math font
*/
var mathbb = function(value, mode, color, classes) {
return makeSymbol(
value, "AMS-Regular", mode, color, classes.concat(["amsrm"]));
};

/**
* Makes a symbol in the calligraphic font
*/
var mathcal = function(value, mode, color, classes) {
return makeSymbol(
value, "Calligraphic-Regular", mode, color, classes.concat(["mathcal"]));
};

/**
* Makes a symbol in the fraktur font
*/
var mathfrak = function(value, mode, color, classes) {
return makeSymbol(
value, "Fraktur-Regular", mode, color, classes.concat(["mathfrak"]));
};

/**
* Calculate the height, depth, and maxFontSize of an element based on its
* children.
Expand Down Expand Up @@ -312,6 +336,9 @@ module.exports = {
makeSymbol: makeSymbol,
mathit: mathit,
mathrm: mathrm,
mathbb: mathbb,
mathcal: mathcal,
mathfrak: mathfrak,
makeSpan: makeSpan,
makeFragment: makeFragment,
makeVList: makeVList,
Expand Down
32 changes: 28 additions & 4 deletions src/buildHTML.js
Expand Up @@ -160,13 +160,32 @@ var isCharacterBox = function(group) {
*/
var groupTypes = {
mathord: function(group, options, prev) {
return buildCommon.mathit(
group.value, group.mode, options.getColor(), ["mord"]);
if (options.font === "mathrm") {
return buildCommon.mathrm(
group.value, group.mode, options.getColor(), ["mord"]);
} else if (options.font === "mathbb") {
return buildCommon.mathbb(
group.value, group.mode, options.getColor(), ["mord"]);
} else if (options.font === "mathcal") {
return buildCommon.mathcal(
group.value, group.mode, options.getColor(), ["mord"]);
} else if (options.font === "mathfrak") {
return buildCommon.mathfrak(
group.value, group.mode, options.getColor(), ["mord"]);
} else {
return buildCommon.mathit(
group.value, group.mode, options.getColor(), ["mord"]);
}
},

textord: function(group, options, prev) {
return buildCommon.mathrm(
group.value, group.mode, options.getColor(), ["mord"]);
if (options.font === "mathit") {
return buildCommon.mathit(
group.value, group.mode, options.getColor(), ["mord"]);
} else {
return buildCommon.mathrm(
group.value, group.mode, options.getColor(), ["mord"]);
}
},

bin: function(group, options, prev) {
Expand Down Expand Up @@ -884,6 +903,11 @@ var groupTypes = {
return makeSpan([options.style.reset(), newStyle.cls()], inner);
},

font: function(group, options, prev) {
var font = group.value.font;
return buildGroup(group.value.body, options.withFont(font), prev);
},

delimsizing: function(group, options, prev) {
var delim = group.value.value;

Expand Down
25 changes: 24 additions & 1 deletion src/buildMathML.js
Expand Up @@ -23,6 +23,18 @@ var makeText = function(text, mode) {
return new mathMLTree.TextNode(text);
};

// Keep track of the current font specified by /mathbb, /mathit, or /mathrm
var fontStack = [];

// A map between tex font commands an MathML mathvariant attribute values
var fontMap = {
"mathbb": "double-struck",
"mathit": "italic",
"mathrm": "normal",
"mathcal": "script",
"mathfrak": "fraktur"
};

/**
* Functions for handling the different types of groups found in the parse
* tree. Each function should take a parse group and return a MathML node.
Expand All @@ -32,7 +44,11 @@ var groupTypes = {
var node = new mathMLTree.MathNode(
"mi",
[makeText(group.value, group.mode)]);


if (fontStack.length > 0) {
var font = fontStack[fontStack.length - 1];
node.setAttribute("mathvariant", font);
}
return node;
},

Expand Down Expand Up @@ -287,6 +303,13 @@ var groupTypes = {
return node;
},

font: function(group) {
fontStack.push(fontMap[group.value.font]);
var node = buildGroup(group.value.body);
fontStack.pop();
return node;
},

delimsizing: function(group) {
var children = [];

Expand Down
2 changes: 1 addition & 1 deletion src/fontMetrics.js

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions src/functions.js
Expand Up @@ -463,6 +463,22 @@ var duplicatedFunctions = [
}
},

{
funcs: [
"\\mathrm", "\\mathbb", "\\mathit", "\\mathcal", "\\mathfrak"
],
data: {
numArgs: 1,
handler: function (func, body) {
return {
type: "font",
font: func.slice(1),
body: body
};
}
}
},

// Accents
{
funcs: [
Expand Down
8 changes: 4 additions & 4 deletions static/fonts.less
Expand Up @@ -48,10 +48,10 @@
}

.font-face('AMS', normal, normal);
// .font-face('Caligraphic', bold, normal);
// .font-face('Caligraphic', normal, normal);
// .font-face('Fraktur', bold, normal);
// .font-face('Fraktur', normal, normal);
.font-face('Caligraphic', bold, normal);
.font-face('Caligraphic', normal, normal);
.font-face('Fraktur', bold, normal);
.font-face('Fraktur', normal, normal);
.font-face('Main', bold, normal);
.font-face('Main', normal, italic);
.font-face('Main', normal, normal);
Expand Down
8 changes: 8 additions & 0 deletions static/katex.less
Expand Up @@ -57,6 +57,14 @@
font-family: KaTeX_AMS;
}

.mathcal {
font-family: KaTeX_Caligraphic;
}

.mathfrak {
font-family: KaTeX_Fraktur;
}

// This value is also used in fontMetrics.js, if you change it make sure the
// values match.
@ptperem: 10.0;
Expand Down
104 changes: 104 additions & 0 deletions test/katex-spec.js
Expand Up @@ -1070,6 +1070,110 @@ describe("A style change parser", function() {
});
});

describe("A font parser", function () {
it("should parse \\mathrm, \\mathbb, and \\mathit", function () {
expect("\\mathrm x").toParse();
expect("\\mathbb x").toParse();
expect("\\mathit x").toParse();
expect("\\mathrm {x + 1}").toParse();
expect("\\mathbb {x + 1}").toParse();
expect("\\mathit {x + 1}").toParse();
});

it("should parse \\mathcal and \\mathfrak", function () {
expect("\\mathcal{ABC123}").toParse();
expect("\\mathfrak{abcABC123}").toParse();
});

it("should produce the correct fonts", function () {
var mathbbParse = parseTree("\\mathbb x")[0];
expect(mathbbParse.value.font).toMatch("mathbb");
expect(mathbbParse.value.type).toMatch("font");

var mathrmParse = parseTree("\\mathrm x")[0];
expect(mathrmParse.value.font).toMatch("mathrm");
expect(mathrmParse.value.type).toMatch("font");

var mathitParse = parseTree("\\mathit x")[0];
expect(mathitParse.value.font).toMatch("mathit");
expect(mathitParse.value.type).toMatch("font");

var mathcalParse = parseTree("\\mathcal C")[0];
expect(mathcalParse.value.font).toMatch("mathcal");
expect(mathcalParse.value.type).toMatch("font");

var mathfrakParse = parseTree("\\mathfrak C")[0];
expect(mathfrakParse.value.font).toMatch("mathfrak");
expect(mathfrakParse.value.type).toMatch("font");
});

it("should parse nested font commands", function () {
var nestedParse = parseTree("\\mathbb{R \\neq \\mathrm{R}}")[0];
expect(nestedParse.value.font).toMatch("mathbb");
expect(nestedParse.value.type).toMatch("font");

expect(nestedParse.value.body.value.length).toMatch(3);
var bbBody = nestedParse.value.body.value;
expect(bbBody[0].type).toMatch("mathord");
expect(bbBody[1].type).toMatch("rel");
expect(bbBody[2].type).toMatch("font");
expect(bbBody[2].value.font).toMatch("mathrm");
expect(bbBody[2].value.type).toMatch("font");
});

it("should work with \\color", function () {
var colorMathbbParse = parseTree("\\color{blue}{\\mathbb R}")[0];
expect(colorMathbbParse.value.type).toMatch("color");
expect(colorMathbbParse.value.color).toMatch("blue");
var body = colorMathbbParse.value.value;
expect(body.length).toMatch(1);
expect(body[0].value.type).toMatch("font");
expect(body[0].value.font).toMatch("mathbb");
});

it("should not parse a series of font commands", function () {
expect("\\mathbb \\mathrm R").toNotParse();
});
});

describe("A font tree-builder", function () {
it("should render \\mathbb{R} with the correct font", function () {
var tree = parseTree("\\mathbb{R}");
var markup = buildHTML(tree, defaultSettings).toMarkup();
expect(markup).toContain("<span class=\"mord amsrm\">R</span>");
});

it("should render \\mathrm{R} with the correct font", function () {
var tree = parseTree("\\mathrm{R}");
var markup = buildHTML(tree, defaultSettings).toMarkup();
expect(markup).toContain("<span class=\"mord\">R</span>");
});

it("should render \\mathcal{R} with the correct font", function () {
var tree = parseTree("\\mathcal{R}");
var markup = buildHTML(tree, defaultSettings).toMarkup();
expect(markup).toContain("<span class=\"mord mathcal\">R</span>");
});

it("should render \\mathfrak{R} with the correct font", function () {
var tree = parseTree("\\mathfrak{R}");
var markup = buildHTML(tree, defaultSettings).toMarkup();
expect(markup).toContain("<span class=\"mord mathfrak\">R</span>");
});

it("should render a combination of font and color changes", function () {
var tree = parseTree("\\color{blue}{\\mathbb R}");
var markup = buildHTML(tree, defaultSettings).toMarkup();
var span = "<span class=\"mord amsrm\" style=\"color:blue;\">R</span>";
expect(markup).toContain(span);

tree = parseTree("\\mathbb{\\color{blue}{R}}");
markup = buildHTML(tree, defaultSettings).toMarkup();
span = "<span class=\"mord amsrm\" style=\"color:blue;\">R</span>";
expect(markup).toContain(span);
});
});

describe("A bin builder", function() {
it("should create mbins normally", function() {
var built = getBuilt("x + y");
Expand Down
Binary file added test/screenshotter/images/MathBB-firefox.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit ecc5170

Please sign in to comment.