diff --git a/lib/context2d.js b/lib/context2d.js index 3ecb37922..e95421e61 100644 --- a/lib/context2d.js +++ b/lib/context2d.js @@ -16,6 +16,10 @@ var canvas = require('./bindings') , CanvasPattern = canvas.CanvasPattern , ImageData = canvas.ImageData; +var parseCssFont = require('parse-css-font'); + +var unitsCss = require('units-css'); + /** * Export `Context2d` as the module. */ @@ -34,26 +38,6 @@ var cache = {}; var baselines = ['alphabetic', 'top', 'bottom', 'middle', 'ideographic', 'hanging']; -/** - * Font RegExp helpers. - */ - -var weights = 'normal|bold|bolder|lighter|[1-9]00' - , styles = 'normal|italic|oblique' - , units = 'px|pt|pc|in|cm|mm|%' - , string = '\'([^\']+)\'|"([^"]+)"|[\\w-]+'; - -/** - * Font parser RegExp; - */ - -var fontre = new RegExp('^ *' - + '(?:(' + weights + ') *)?' - + '(?:(' + styles + ') *)?' - + '([\\d\\.]+)(' + units + ') *' - + '((?:' + string + ')( *, *(?:' + string + '))*)' - ); - /** * Parse font `str`. * @@ -62,42 +46,51 @@ var fontre = new RegExp('^ *' * @api private */ -var parseFont = exports.parseFont = function(str){ - var font = {} - , captures = fontre.exec(str); +var parseFont = exports.parseFont = function(str) { + var parsedFont; - // Invalid - if (!captures) return; + // Try to parse the font string using parse-css-font. + // It will throw an exception if it fails. + try { + parsedFont = parseCssFont(str); + } + catch (e) { + // Invalid + return; + } // Cached if (cache[str]) return cache[str]; - // Populate font object - font.weight = captures[1] || 'normal'; - font.style = captures[2] || 'normal'; - font.size = parseFloat(captures[3]); - font.unit = captures[4]; - font.family = captures[5].replace(/["']/g, '').split(',').map(function (family) { - return family.trim(); - }).join(','); + // Parse size into value and unit using units-css + var size = unitsCss.parse(parsedFont.size); // TODO: dpi // TODO: remaining unit conversion - switch (font.unit) { + switch (size.unit) { case 'pt': - font.size /= .75; + size.value /= .75; break; case 'in': - font.size *= 96; + size.value *= 96; break; case 'mm': - font.size *= 96.0 / 25.4; + size.value *= 96.0 / 25.4; break; case 'cm': - font.size *= 96.0 / 2.54; + size.value *= 96.0 / 2.54; break; } + // Populate font object + var font = { + weight: parsedFont.weight, + style: parsedFont.style, + size: size.value, + unit: size.unit, + family: parsedFont.family.join(',') + }; + return cache[str] = font; }; diff --git a/package.json b/package.json index a946bf81b..f87c02633 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,9 @@ "test-server": "node test/server.js" }, "dependencies": { - "nan": "^2.4.0" + "nan": "^2.4.0", + "parse-css-font": "^2.0.2", + "units-css": "^0.4.0" }, "devDependencies": { "express": "^4.14.0", diff --git a/test/public/tests.js b/test/public/tests.js index 4f24d7ab1..68170c3fe 100644 --- a/test/public/tests.js +++ b/test/public/tests.js @@ -999,6 +999,18 @@ tests['font family invalid'] = function (ctx) { ctx.fillText('14px Invalid, Impact', 100, 100) } +tests['font style variant weight size family'] = function (ctx) { + ctx.strokeStyle = '#666' + ctx.strokeRect(0, 0, 200, 200) + ctx.lineTo(0, 100) + ctx.lineTo(200, 100) + ctx.stroke() + + ctx.font = 'normal normal normal 16px Impact' + ctx.textAlign = 'center' + ctx.fillText('normal normal normal 16px', 100, 100) +} + tests['globalCompositeOperation source-over'] = function (ctx) { ctx.fillStyle = 'blue' ctx.fillRect(0, 0, 100, 100)