I'm using FabricJS to allow a user to design an SVG in the browser. When I'm looking to save I'm trying to use OpenType JS to convert the textbox (Fabric) into an SVG Path using OpenType.
Problem I'm seeing is the location of my textbox is not translating through to the new path addition to the canvas.
AND
When I add the new path to the canvas, then call toSVG() it disappears in the resulting SVG I save.
Code:
async function convertTextToPaths() {
ungroup();
var _all = canvas.getObjects();
for(i=0;i<_all.length;i++) {
var activeObject = _all[i];
if(activeObject.type=="textbox") {
const font = await opentype.load('fonts/'+activeObject.fontFamily+'.ttf');
debugger;
console.log(activeObject.type, activeObject.left, activeObject.top+activeObject.height, activeObject.fontSize);
const path = font.getPath(activeObject.text, activeObject.left, activeObject.top+activeObject.height, activeObject.fontSize);
const outlinetextpath = new fabric.Path(path.toPathData(3));
activeObject.dirty=true;
canvas.remove(activeObject);
canvas.insertAt(outlinetextpath,2);
canvas.renderAll();
}
}
}
Make any sense or can someone share some thoughts?
thank you
Fabric.js generated boundingBoxes differ from those generated by opentype.js
You need to calculate some scaling factors/ratios according to your font's metrics for appropriate vertical alignments.
Calling
font.getPath(string, x, y, fontSize)
will "draw" a path from bottom to top:Example: draw text element at x=500, y=250
(canvas size: 1000×500px; font-family: Fira Sans; font-size: 100px;)
fabric.js
opentype.js
Red: opentype.js path; Black: fabric generated textBox
Left:
top: 250
Right:
object.top+object.height
The opentype.js generated element is vertically aligned to 250 px using the font's baseline as a reference point.
Whereas fabric.js aligns the
textBox
element according to it's top (boundary box) y coordinate.Working example
(Download function won't work on SO due to content security policies)
Codepen example
How it works:
Essentially, we need to compare heights as rendered by fabrics.js
testBox
objects with ideally rendered boundaries based on font metrics.First we need to get some ratios to translate font units to pixels.
Most importantly we need to calculate a ratio/factor to translate relative font metric values to absolute font size related pixel values:
1. Font metrics: font size to font unit ratio
let unitsPerEm = font.unitsPerEm; let ratio = fontSize / unitsPerEm;
Most webfonts have a 1000 unitsPerEm value.
However, traditional truetype fonts (so not particularly optimised for web usage) usually use 2048 units per em.
2. Font metrics: ascender and descender
With regards to the font's metrics an ideal bBox would have the height of
(ascender + descender) * FontSize2unitsPerEmRatio
.3. Font metrics to canvas coordinates
Fabric.js bBox is slightly bigger – so we need to compare their heights to get the perfect scaling factor.
Now we get the right y offset when using
getPath()