canvas-text-opentypejs-shim
Advanced tools
Comparing version 0.1.1 to 0.1.2
@@ -8,13 +8,2 @@ (function (root, factory) { | ||
}(this, function () { | ||
// adopted from https://github.com/kangax/fabric.js/blob/v1.6.6/src/parser.js#L703 | ||
var num = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)' | ||
var fontCSS = new RegExp( | ||
'(normal|italic|oblique)?\\s*' + | ||
'(normal|small-caps)?\\s*' + | ||
'(normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900)?\\s*' + | ||
'(normal|ultra-condensed|extra-condensed|condensed|semi-condensed|' + | ||
'semi-expanded|expanded|extra-expanded|ultra-expanded)?\\s*' + | ||
'(' + num + '(?:px|cm|mm|em|pt|pc|in)*)(?:\\/(normal|' + num + '))?\\s+' + | ||
'(.*)') | ||
/** | ||
@@ -39,12 +28,62 @@ * @see https://developer.mozilla.org/en-US/docs/Web/CSS/font | ||
function parseCSSFont (font) { | ||
var m = font.match(fontCSS) | ||
return { | ||
fontStyle: m[1] || 'normal', | ||
fontVariant: m[2] || 'normal', | ||
fontWeight: m[3] || 'normal', | ||
fontStretch: m[4] || 'normal', | ||
fontSize: m[5], | ||
lineHeight: m[6] || 'normal', | ||
fontFamily: m[7] | ||
var fontFamilyIndex = [ | ||
font.lastIndexOf(' ', font.indexOf(',')) + 1 || -1, | ||
font.indexOf('"'), | ||
font.indexOf('\'') | ||
].reduce( | ||
function (r, v) { return ~v && v < r ? v : r }, | ||
font.lastIndexOf(' ') | ||
) | ||
var split = font.slice(0, fontFamilyIndex).trim().split(/\s+/) | ||
var fontSize = split[split.length - 1] | ||
var lineHeightIndex = fontSize.indexOf('/') | ||
var o = { | ||
fontStyle: 'normal', | ||
fontVariant: 'normal', | ||
fontWeight: 'normal', | ||
fontStretch: 'normal', | ||
fontSize: ~lineHeightIndex | ||
? fontSize.slice(0, lineHeightIndex) : fontSize, | ||
lineHeight: ~lineHeightIndex | ||
? fontSize.slice(lineHeightIndex + 1) : 'normal', | ||
fontFamily: font.slice(fontFamilyIndex) | ||
} | ||
split.slice(0, -1).forEach(function (v) { | ||
switch (v) { | ||
case 'normal': | ||
break | ||
case 'italic': | ||
case 'oblique': | ||
o.fontStyle = v | ||
break | ||
case 'small-caps': | ||
o.fontVariant = v | ||
break | ||
case 'bold': | ||
case 'bolder': | ||
case 'lighter': | ||
case '100': | ||
case '200': | ||
case '300': | ||
case '400': | ||
case '500': | ||
case '600': | ||
case '700': | ||
case '800': | ||
case '900': | ||
o.fontWeight = v | ||
break | ||
case 'ultra-condensed': | ||
case 'extra-condensed': | ||
case 'condensed': | ||
case 'semi-condensed': | ||
case 'semi-expanded': | ||
case 'expanded': | ||
case 'extra-expanded': | ||
case 'ultra-expanded': | ||
o.fontStretch = v | ||
break | ||
} | ||
}) | ||
return o | ||
} | ||
@@ -51,0 +90,0 @@ |
@@ -1,1 +0,1 @@ | ||
!function(t,n){"object"==typeof module&&module.exports?module.exports=n():t["canvas-text-opentypejs-shim"]=n()}(this,function(){function t(t){var n=t.match(o);return{fontStyle:n[1]||"normal",fontVariant:n[2]||"normal",fontWeight:n[3]||"normal",fontStretch:n[4]||"normal",fontSize:n[5],lineHeight:n[6]||"normal",fontFamily:n[7]}}function n(t,n,e){for(var o=0,a=0,i=0,r=n.stringToGlyphs(t),l=0;l<r.length;l++){var s=r[l];s.advanceWidth&&(i+=s.advanceWidth),l<r.length-1&&(i+=n.getKerningValue(s,r[l+1])),s.yMax&&(o=Math.max(o,s.yMax)),s.yMin&&(a=Math.min(a,s.yMin))}var d=1/n.unitsPerEm*e;return{width:i*d,actualBoundingBoxAscent:o*d,actualBoundingBoxDescent:a*d,fontBoundingBoxAscent:n.ascender*d,fontBoundingBoxDescent:n.descender*d}}var e="(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)",o=new RegExp("(normal|italic|oblique)?\\s*(normal|small-caps)?\\s*(normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900)?\\s*(normal|ultra-condensed|extra-condensed|condensed|semi-condensed|semi-expanded|expanded|extra-expanded|ultra-expanded)?\\s*("+e+"(?:px|cm|mm|em|pt|pc|in)*)(?:\\/(normal|"+e+"))?\\s+(.*)");return function(e,o){function a(t,o,a,i,r){var d=a,c=o;if("alphabetic"!==e.textBaseline||"start"!==e.textAlign&&"left"!==e.textAlign){var u=n(t,s,l);"top"===e.textBaseline?d+=u.actualBoundingBoxAscent-u.fontBoundingBoxDescent:"hanging"===e.textBaseline?d+=u.actualBoundingBoxAscent:"middle"===e.textBaseline?d+=u.actualBoundingBoxAscent/2:"ideographic"!==e.textBaseline&&"bottom"!==e.textBaseline||(d+=u.fontBoundingBoxDescent),"right"===e.textAlign||"end"===e.textAlign?c-=u.width:"center"===e.textAlign&&(c-=u.width/2)}var f=s.getPath(t,c,d,l);r?(f.fill=null,f.stroke=e.strokeStyle):f.fill=e.fillStyle,f.draw(e)}function i(n){if(r!==n){var e=t(n);if(!/\d+px/.test(e.fontSize))throw new Error('font-size must be in "px"');if(s=o.resolveFont(e),!s&&d)throw new Error("Font instance wasn't found (\""+e.fontFamily+'")');l=parseInt(e.fontSize,10)}r=n}var r,l,s,d="boolean"!=typeof o.strict||o.strict,c=!0,u=e.measureText;e.measureText=function(t){return c&&(i(e.font),s)?n(t,s,l):u.apply(this,arguments)};var f=e.fillText;e.fillText=function(t,n,o,r){return c&&(i(e.font),s)?a(t,n,o,r,!1):f.apply(this,arguments)};var x=e.strokeText;return e.strokeText=function(t,n,o,r){return c&&(i(e.font),s&&a(t,n,o,r,!0)),x.apply(this,arguments)},{enable:function(t){c=!!t}}}}); | ||
!function(e,t){"object"==typeof module&&module.exports?module.exports=t():e["canvas-text-opentypejs-shim"]=t()}(this,function(){function e(e){var t=[e.lastIndexOf(" ",e.indexOf(","))+1||-1,e.indexOf('"'),e.indexOf("'")].reduce(function(e,t){return~t&&t<e?t:e},e.lastIndexOf(" ")),n=e.slice(0,t).trim().split(/\s+/),a=n[n.length-1],i=a.indexOf("/"),o={fontStyle:"normal",fontVariant:"normal",fontWeight:"normal",fontStretch:"normal",fontSize:~i?a.slice(0,i):a,lineHeight:~i?a.slice(i+1):"normal",fontFamily:e.slice(t)};return n.slice(0,-1).forEach(function(e){switch(e){case"normal":break;case"italic":case"oblique":o.fontStyle=e;break;case"small-caps":o.fontVariant=e;break;case"bold":case"bolder":case"lighter":case"100":case"200":case"300":case"400":case"500":case"600":case"700":case"800":case"900":o.fontWeight=e;break;case"ultra-condensed":case"extra-condensed":case"condensed":case"semi-condensed":case"semi-expanded":case"expanded":case"extra-expanded":case"ultra-expanded":o.fontStretch=e}}),o}function t(e,t,n){for(var a=0,i=0,o=0,s=t.stringToGlyphs(e),r=0;r<s.length;r++){var c=s[r];c.advanceWidth&&(o+=c.advanceWidth),r<s.length-1&&(o+=t.getKerningValue(c,s[r+1])),c.yMax&&(a=Math.max(a,c.yMax)),c.yMin&&(i=Math.min(i,c.yMin))}var l=1/t.unitsPerEm*n;return{width:o*l,actualBoundingBoxAscent:a*l,actualBoundingBoxDescent:i*l,fontBoundingBoxAscent:t.ascender*l,fontBoundingBoxDescent:t.descender*l}}return function(n,a){function i(e,a,i,o,s){var l=i,f=a;if("alphabetic"!==n.textBaseline||"start"!==n.textAlign&&"left"!==n.textAlign){var d=t(e,c,r);"top"===n.textBaseline?l+=d.actualBoundingBoxAscent-d.fontBoundingBoxDescent:"hanging"===n.textBaseline?l+=d.actualBoundingBoxAscent:"middle"===n.textBaseline?l+=d.actualBoundingBoxAscent/2:"ideographic"!==n.textBaseline&&"bottom"!==n.textBaseline||(l+=d.fontBoundingBoxDescent),"right"===n.textAlign||"end"===n.textAlign?f-=d.width:"center"===n.textAlign&&(f-=d.width/2)}var u=c.getPath(e,f,l,r);s?(u.fill=null,u.stroke=n.strokeStyle):u.fill=n.fillStyle,u.draw(n)}function o(t){if(s!==t){var n=e(t);if(!/\d+px/.test(n.fontSize))throw new Error('font-size must be in "px"');if(c=a.resolveFont(n),!c&&l)throw new Error("Font instance wasn't found (\""+n.fontFamily+'")');r=parseInt(n.fontSize,10)}s=t}var s,r,c,l="boolean"!=typeof a.strict||a.strict,f=!0,d=n.measureText;n.measureText=function(e){return f&&(o(n.font),c)?t(e,c,r):d.apply(this,arguments)};var u=n.fillText;n.fillText=function(e,t,a,s){return f&&(o(n.font),c)?i(e,t,a,s,!1):u.apply(this,arguments)};var x=n.strokeText;return n.strokeText=function(e,t,a,s){return f&&(o(n.font),c&&i(e,t,a,s,!0)),x.apply(this,arguments)},{enable:function(e){f=!!e}}}}); |
@@ -11,12 +11,39 @@ // https://github.com/shyiko/canvas-text-opentypejs-shim | ||
const fs = require('fs') | ||
const applyCanvasTextOpenTypeJsShim = | ||
const useOpenTypeJsForText = | ||
(() => { try { return require('../canvas-text-opentypejs-shim') } catch (e) {} })() || | ||
require('canvas-text-opentypejs-shim') | ||
fetch('https://fonts.gstatic.com/s/roboto' + | ||
'/v15/QHD8zigcbDB8aPfIoaupKOvvDin1pK8aKteLpeZ5c0A.ttf') | ||
// this way fabric tends to yield more consistent results when compared to | ||
// CanvasRenderingContext2D.drawText | ||
fabric.Text.prototype._fontSizeMult = 1.25 | ||
/** | ||
* @param fontFamily e.g. "'Open Sans', sans-serif" | ||
* @return e.g. ["Open Sans", "sans-serif"] | ||
*/ | ||
function splitFontFamily (fontFamily) { | ||
return fontFamily.split(',') | ||
.map((f) => f.trim().replace(/^['"]/, '').replace(/['"]$/, '')) | ||
.filter(Boolean) | ||
} | ||
// making sure whatever context is used to measure/draw text it has shim active | ||
// (given openTypeJsShimConfig was provided) | ||
fabric.Text.prototype._setTextStyles = (function (original) { | ||
return function (ctx) { | ||
var cfg = this.openTypeJsShimConfig | ||
if (cfg && !ctx.measureText.__openTypeJsShimConfig) { | ||
useOpenTypeJsForText(ctx, cfg) | ||
ctx.measureText.__openTypeJsShimConfig = cfg | ||
} | ||
return original.call(this, ctx) | ||
} | ||
})(fabric.Text.prototype._setTextStyles) | ||
fetch('https://rawgit.com/google/fonts/master/apache/' + | ||
'opensans/OpenSans-Regular.ttf') | ||
.then((res) => new Promise((resolve, reject) => { | ||
res.body | ||
.on('error', reject) | ||
.pipe(fs.createWriteStream('Roboto.ttf')) | ||
.pipe(fs.createWriteStream('OpenSans-Regular.ttf')) | ||
.on('error', reject) | ||
@@ -26,18 +53,19 @@ .on('finish', resolve) | ||
.then(() => { | ||
const font = opentype.loadSync('Roboto.ttf') | ||
const font = opentype.loadSync('OpenSans-Regular.ttf') | ||
const canvas = fabric.createCanvasForNode(this.width, this.height) | ||
applyCanvasTextOpenTypeJsShim(canvas.contextContainer, { | ||
const ctx = fabric.createCanvasForNode(200, 200) | ||
const shimConfig = { | ||
resolveFont: function (o) { | ||
if (o.fontFamily.trim().replace(/^['"]/, '').replace(/['"]$/, '') === 'Roboto') { | ||
if (splitFontFamily(o.fontFamily)[0] === 'Open Sans') { | ||
return font | ||
} | ||
} | ||
}) | ||
} | ||
canvas.add(new fabric.Text('Hello World', { | ||
left: 0, top: 0, fontFamily: 'Roboto', fontSize: 26 | ||
ctx.add(new fabric.Text('Hello World', { | ||
left: -0.5, top: -0.5, fontFamily: 'Open Sans', fontSize: 26, | ||
openTypeJsShimConfig: shimConfig | ||
})) | ||
console.log(`<img src="${canvas.toDataURL()}">`) | ||
console.log(`<img src="${ctx.toDataURL()}">`) | ||
}) |
@@ -11,12 +11,22 @@ // https://github.com/shyiko/canvas-text-opentypejs-shim | ||
const fs = require('fs') | ||
const applyCanvasTextOpenTypeJsShim = | ||
const useOpenTypeJsForText = | ||
(() => { try { return require('../canvas-text-opentypejs-shim') } catch (e) {} })() || | ||
require('canvas-text-opentypejs-shim') | ||
fetch('https://fonts.gstatic.com/s/roboto' + | ||
'/v15/QHD8zigcbDB8aPfIoaupKOvvDin1pK8aKteLpeZ5c0A.ttf') | ||
/** | ||
* @param fontFamily e.g. "'Open Sans', sans-serif" | ||
* @return e.g. ["Open Sans", "sans-serif"] | ||
*/ | ||
function splitFontFamily (fontFamily) { | ||
return fontFamily.split(',') | ||
.map((f) => f.trim().replace(/^['"]/, '').replace(/['"]$/, '')) | ||
.filter(Boolean) | ||
} | ||
fetch('https://rawgit.com/google/fonts/master/apache/' + | ||
'opensans/OpenSans-Regular.ttf') | ||
.then((res) => new Promise((resolve, reject) => { | ||
res.body | ||
.on('error', reject) | ||
.pipe(fs.createWriteStream('Roboto.ttf')) | ||
.pipe(fs.createWriteStream('OpenSans-Regular.ttf')) | ||
.on('error', reject) | ||
@@ -26,9 +36,9 @@ .on('finish', resolve) | ||
.then(() => { | ||
const font = opentype.loadSync('Roboto.ttf') | ||
const font = opentype.loadSync('OpenSans-Regular.ttf') | ||
const canvas = new NodeCanvas(200, 200) | ||
const ctx = canvas.getContext('2d') | ||
applyCanvasTextOpenTypeJsShim(ctx, { | ||
useOpenTypeJsForText(ctx, { | ||
resolveFont: function (o) { | ||
if (o.fontFamily === 'Roboto') { | ||
if (splitFontFamily(o.fontFamily)[0] === 'Open Sans') { | ||
return font | ||
@@ -39,7 +49,7 @@ } | ||
ctx.font = '26px Roboto' | ||
ctx.font = '26px "Open Sans"' | ||
ctx.fillText('Hello World', 0, 26) | ||
console.log(canvas.toBuffer()) | ||
console.log(`<img src="data:image/png;base64,${canvas.toBuffer().toString('base64')}">`) | ||
}) | ||
{ | ||
"name": "canvas-text-opentypejs-shim", | ||
"version": "0.1.1", | ||
"version": "0.1.2", | ||
"description": "Consistent text rendering for <canvas>", | ||
@@ -5,0 +5,0 @@ "author": "Stanley Shyiko <stanley.shyiko@gmail.com>", |
@@ -22,3 +22,3 @@ # canvas-text-opentypejs-shim | ||
```javascript | ||
const applyCanvasTextOpenTypeJsShim = require('canvas-text-opentypejs-shim') | ||
const useOpenTypeJsForText = require('canvas-text-opentypejs-shim') | ||
@@ -28,3 +28,3 @@ const canvas ... | ||
applyCanvasTextOpenTypeJsShim(ctx, {resolveFont: (o) => opentypeFontInstance}) | ||
useOpenTypeJsForText(ctx, {resolveFont: (o) => opentypeFontInstance}) | ||
@@ -31,0 +31,0 @@ ctx.font = '26px Roboto' |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
111172
349