Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend the GlyphRun object with set-serialization functions? #24

Open
Pomax opened this issue Feb 14, 2016 · 28 comments
Open

Extend the GlyphRun object with set-serialization functions? #24

Pomax opened this issue Feb 14, 2016 · 28 comments

Comments

@Pomax
Copy link
Contributor

Pomax commented Feb 14, 2016

Right now in order to serialize a GlyphRun to something like SVG, one needs to manually run over the glyph array and work with the positional information to generate something that makes sense.

Giving the GlyphRun object its own API in addition to the glyphs and positions properties might be a good idea, so that this works:

var font = fontkit.openSync('font.ttf');
var run = font.layout('hello world');
var svg = run.toSVG();
@devongovett
Copy link
Member

This sounds like an interesting idea.

For SVG in particular, however, there are many possible ways in which someone might want to convert a GlyphRun to SVG. For example, someone might want to change the color of every glyph, or do some other customization/transform. I'm not sure we could easily allow that sort of customization without adding a lot of complexity, and if we didn't allow customization, I'm not sure how many people would be able to use it.

@Pomax
Copy link
Contributor Author

Pomax commented Feb 15, 2016

Sure, but this wouldn't replace the way you can serialize at the moment, merely add to the ways in which you can. If you want fine pathing control, you should still be able to use the syntax that's currently advertised in the README.md, but if you just want to get a shaped piece of text serialized to SVG (or some other format) then not having to roll your own code each time is very nice indeed.

One thing that might be worth doing, though, is renaming the functions to reflect what they really do: toSVG does not actually generate SVG, but a path string. It would be interesting if it generated actual SVG, with <path> elements for each glyph; then people would be able to trivially customize those by simply playing with the SVG document by parsing it with any of several xml/svg parsers (including the browser itself for client-side code). But that's probably a separate issue.

@pwichmann
Copy link

I would like to support this request by Pomax.

I use fontkit as my only available option to import text as an SVG (path) into paper.js. This allows me to load any font into paper.js.
It is crucial for the application that I am working on to be able to export the string as whole - and not just a single glyph.

I wish I could code it myself but I am lacking the necessary skills. If there is anything to facilitate the implementation of this feature, please let us know.

@twardoch
Copy link

twardoch commented Apr 6, 2017

I’ve just proposed OpenType/opentype-layout#4 — which could be an inspiration to provide a compatible function for GlyphRun that would export a JS structure that serializes into JSON the same way as hb-view does it.

In addition, I shall add that hb-view that uses HarfBuzz and Cairo, when invoked:

hb-view --font-file="CormorantGaramond-Regular.otf" --features="+liga" \
  --text="office" --font-size=256 --output-format=svg

outputs:

<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="583.683594pt" height="342pt" viewBox="0 0 583.683594 342" version="1.1">
<defs>
<g>
<symbol overflow="visible" id="glyph0-0">
<path style="stroke:none;" d="M 24.0625 70.40625 L 105.21875 70.40625 L 105.21875 -185.59375 L 24.0625 -185.59375 Z M 43.015625 -160.25 L 43.015625 -168.453125 L 85.25 -168.453125 L 85.25 -160.25 L 68.359375 -160.25 L 68.359375 -150.53125 L 85.25 -150.53125 L 85.25 -142.34375 L 42.75 -142.34375 L 42.75 -150.53125 L 59.640625 -150.53125 L 59.640625 -160.25 Z M 42.75 -109.0625 L 42.75 -135.421875 L 68.359375 -135.421875 L 68.359375 -117.765625 L 85.25 -117.765625 L 85.25 -109.0625 Z M 51.203125 -117.765625 L 59.640625 -117.765625 L 59.640625 -126.71875 L 51.203125 -126.71875 Z M 42.75 -93.4375 L 42.75 -101.890625 L 85.25 -101.890625 L 85.25 -93.4375 L 68.359375 -93.4375 L 68.359375 -75.515625 L 42.75 -75.515625 L 42.75 -84.21875 L 59.640625 -84.21875 L 59.640625 -93.4375 Z M 77.0625 -69.890625 L 77.0625 -86.53125 L 85.25 -86.53125 L 85.25 -61.1875 L 42.75 -61.1875 L 42.75 -69.890625 Z M 59.640625 -40.953125 L 59.640625 -55.296875 L 85.25 -55.296875 L 85.25 -26.375 L 42.75 -26.375 L 42.75 -55.296875 L 51.203125 -55.296875 L 51.203125 -34.8125 L 77.0625 -34.8125 L 77.0625 -47.109375 L 68.359375 -47.109375 L 68.359375 -40.953125 Z M 42.75 17.921875 L 42.75 -11.015625 L 85.25 -11.015625 L 85.25 17.921875 Z M 51.203125 9.46875 L 77.0625 9.46875 L 77.0625 -2.8125 L 51.203125 -2.8125 Z M 42.75 32.25 L 42.75 23.546875 L 85.25 23.546875 L 85.25 32.25 L 77.0625 32.25 L 59.390625 44.28125 L 85.25 44.28125 L 85.25 52.734375 L 42.75 52.734375 L 42.75 44.28125 L 60.671875 32.25 Z M 42.75 32.25 "/>
</symbol>
<symbol overflow="visible" id="glyph0-1">
<path style="stroke:none;" d="M 59.140625 3.328125 C 88.3125 3.328125 113.40625 -16.890625 113.40625 -49.40625 C 113.40625 -76.03125 94.96875 -102.140625 63.75 -102.140625 C 38.90625 -102.140625 9.21875 -86.015625 9.21875 -50.6875 C 9.21875 -21.765625 28.671875 3.328125 59.140625 3.328125 Z M 66.5625 -1.53125 C 42.75 -1.53125 26.375 -27.140625 26.375 -58.625 C 26.375 -83.71875 37.125 -97.28125 55.296875 -97.28125 C 79.109375 -97.28125 96 -74.5 96 -42.234375 C 96 -13.5625 84.484375 -1.53125 66.5625 -1.53125 Z M 66.5625 -1.53125 "/>
</symbol>
<symbol overflow="visible" id="glyph0-2">
<path style="stroke:none;" d="M 153.34375 -181.5 C 177.65625 -181.5 173.0625 -158.71875 186.375 -158.71875 C 190.96875 -158.71875 194.046875 -161.53125 194.046875 -166.65625 C 194.046875 -176.640625 181.25 -185.859375 161.796875 -185.859375 C 144.640625 -185.859375 130.296875 -180.734375 119.8125 -168.953125 C 112.125 -173.0625 100.09375 -176.890625 85.5 -176.890625 C 50.171875 -176.890625 27.640625 -153.59375 25.34375 -107.015625 C 25.09375 -98.296875 21.25 -95.484375 7.171875 -95.484375 C 6.140625 -95.484375 6.140625 -92.15625 7.171875 -92.15625 C 23.546875 -92.15625 25.859375 -90.875 25.859375 -78.34375 L 25.859375 -20.734375 C 25.859375 -5.890625 22.78125 -3.078125 8.1875 -3.078125 C 7.171875 -3.078125 7.171875 0 8.1875 0 C 14.84375 0 23.546875 -0.515625 32.765625 -0.515625 C 44.03125 -0.515625 54.265625 0 65.53125 0 C 66.5625 0 66.5625 -3.078125 65.53125 -3.078125 C 43.515625 -3.078125 40.453125 -5.890625 40.453125 -20.734375 L 40.453125 -91.140625 C 58.375 -91.140625 71.421875 -91.140625 80.125 -90.625 C 100.609375 -90.109375 100.859375 -90.109375 101.375 -78.34375 L 101.375 -20.734375 C 101.375 -5.890625 98.296875 -3.078125 83.71875 -3.078125 C 82.6875 -3.078125 82.6875 0 83.71875 0 C 90.375 0 99.078125 -0.515625 108.28125 -0.515625 C 119.546875 -0.515625 129.796875 0 141.0625 0 C 142.078125 0 142.078125 -3.078125 141.0625 -3.078125 C 119.046875 -3.078125 115.96875 -5.890625 115.96875 -20.734375 L 115.96875 -91.390625 C 126.96875 -90.875 136.703125 -89.09375 147.96875 -85.765625 C 148.734375 -85.25 150.53125 -89.09375 150.53125 -92.421875 C 150.53125 -95.484375 149.765625 -98.5625 148.734375 -98.5625 C 136.703125 -97.28125 126.71875 -96.25 115.96875 -96 L 115.96875 -112.390625 C 115.96875 -160.765625 131.84375 -181.5 153.34375 -181.5 Z M 100.859375 -107.015625 C 100.859375 -104.703125 100.609375 -102.90625 99.84375 -101.375 C 97.796875 -97.53125 92.921875 -96.515625 82.4375 -96.25 C 71.6875 -96 58.875 -96 40.453125 -96 L 40.453125 -112.390625 C 40.453125 -152.578125 55.296875 -172.546875 77.0625 -172.546875 C 90.109375 -172.546875 102.140625 -165.890625 111.109375 -155.640625 C 105.21875 -143.359375 101.625 -127.484375 100.859375 -107.015625 Z M 211.71875 -3.078125 C 198.40625 -3.078125 195.578125 -6.140625 195.578125 -20.734375 L 195.578125 -68.859375 C 195.578125 -85.25 196.09375 -96.25 196.09375 -99.078125 C 196.09375 -100.09375 194.8125 -101.890625 193.28125 -101.125 L 160 -85.5 C 158.46875 -84.734375 159.75 -81.921875 161.28125 -82.4375 C 176.640625 -89.859375 181.25 -86.53125 181.25 -68.609375 L 181.25 -20.734375 C 181.25 -6.140625 178.4375 -3.078125 165.125 -3.078125 C 164.09375 -3.078125 164.09375 0 165.125 0 C 171.265625 0 179.453125 -0.515625 188.421875 -0.515625 C 197.375 -0.515625 205.5625 0 211.71875 0 C 212.734375 0 212.734375 -3.078125 211.71875 -3.078125 Z M 211.71875 -3.078125 "/>
</symbol>
<symbol overflow="visible" id="glyph0-3">
<path style="stroke:none;" d="M 59.140625 3.078125 C 73.46875 3.078125 84.984375 -1.53125 97.03125 -12.28125 C 97.796875 -13.0625 96 -14.84375 95.234375 -14.34375 C 87.296875 -7.9375 78.078125 -5.890625 69.125 -5.890625 C 38.65625 -5.890625 26.109375 -28.671875 26.109375 -55.046875 C 26.109375 -79.359375 37.125 -96 56.578125 -96 C 66.8125 -96 70.90625 -91.90625 74.75 -85.765625 C 77.0625 -81.15625 80.640625 -77.3125 86.015625 -77.3125 C 91.640625 -77.3125 94.96875 -81.15625 94.96875 -85.5 C 94.96875 -94.71875 79.359375 -101.125 65.03125 -101.125 C 38.90625 -101.125 9.21875 -81.65625 9.21875 -47.359375 C 9.21875 -23.296875 24.0625 3.078125 59.140625 3.078125 Z M 59.140625 3.078125 "/>
</symbol>
<symbol overflow="visible" id="glyph0-4">
<path style="stroke:none;" d="M 91.640625 -14.34375 C 84.21875 -9.734375 77.5625 -5.890625 66.046875 -5.890625 C 40.1875 -5.890625 26.109375 -26.875 26.109375 -55.8125 C 26.109375 -59.140625 26.375 -62.203125 26.625 -65.03125 L 90.109375 -65.28125 C 92.421875 -65.28125 92.921875 -67.328125 92.921875 -71.9375 C 92.921875 -90.109375 82.6875 -101.125 63.234375 -101.125 C 33.28125 -101.125 9.21875 -77.0625 9.21875 -46.078125 C 9.21875 -20.984375 25.34375 3.078125 56.0625 3.078125 C 69.125 3.078125 81.40625 -1.28125 93.4375 -11.78125 C 94.203125 -12.796875 92.421875 -14.84375 91.640625 -14.34375 Z M 54.78125 -96 C 67.578125 -96 75.78125 -87.046875 75.78125 -69.375 L 27.140625 -68.359375 C 30.203125 -85.765625 39.9375 -96 54.78125 -96 Z M 54.78125 -96 "/>
</symbol>
</g>
</defs>
<g id="surface1">
<rect x="0" y="0" width="583.683594" height="342" style="fill:rgb(100%,100%,100%);fill-opacity:1;stroke:none;"/>
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
  <use xlink:href="#glyph0-1" x="16" y="252.75"/>
  <use xlink:href="#glyph0-2" x="138.625" y="252.75"/>
  <use xlink:href="#glyph0-3" x="359.554688" y="252.75"/>
  <use xlink:href="#glyph0-4" x="463.234375" y="252.75"/>
</g>
</g>
</svg>

This output is pretty good, using the SVG <symbol> elements to define the glyph outlines, and then using the <use> element to refer to them and position them. The naming of the symbol elements could be better (e.g. using the font’s glyph names), but overall, I think this would be the way to go.

@Pomax
Copy link
Contributor Author

Pomax commented Apr 6, 2017

sort of agreed, if it came without those hardcoded style attributes, because using this in your own application (certainly embedding in a modern HTML5 document) will make applying your own styling unreasonably hard.

@twardoch
Copy link

twardoch commented Apr 6, 2017

@Pomax Of course, I agree. In case of hb-view, it really is Cairo that is doing the final SVG writing, so the output isn’t really “designed” but sort of accidental. But it happens to provide a useful template.

@khaledhosny
Copy link
Contributor

khaledhosny commented Apr 6, 2017

Cairo SVGs are also problematic when embedded in HTML since the ids used for glyphs are not unique and one needs to post-process them before embedding more than one SVG in an HTML page.

@pwichmann
Copy link

pwichmann commented Apr 6, 2017

Could anyone explain like I'm five what the best way to import a whole string as SVG paths is, please?

My environment is node.js, paper.js, and fontkit so far. Does OpenType.js allow to import a whole string as SVG paths? Are there any other options?

@Pomax
Copy link
Contributor Author

Pomax commented Apr 6, 2017

OpenType.js definitely lets you get the SVG for a fully typeset string (constrained by its support of the OpenType spec of course). You can see an example of this on their homepage (with the code hosted on github so easy to inspect). FontKit right now does not do SVG generation for fully typeset strings to the best of my knowledge. @devongovett is that correct?

@pwichmann
Copy link

Thanks, @Pomax !

The problem I had with OpenType.js was that it required a canvas - but I wanted to use it on the server-side rather than in the browser. I still don't know how to get this to work. (I managed with paper.js but not OpenType.)

I feel quite dumb at this point.

@Pomax
Copy link
Contributor Author

Pomax commented Apr 6, 2017

No need to feel dumb, paper.js is an amazing library and I fully expect it to be using the canvas as "an afterthought" purely for screen dumping rather than for "doing any actual work". OpenType.js might not be quite as sophisticated. You can try to shim the canvas with something like https://github.com/Automattic/node-canvas although I haven't tried that myself - the alternative is to help figure out how to update the code here in fontkit to serialize post-shaped-sequences to a collection of paths that accurately reflect the shaped text, and then ideally in a way that stays compatible with the universal JSON suggestion by Adam.

@DominikLevitsky
Copy link

This is a feature, that would be really nice to have. Right now I am iterating through each glyph in the run, getting the commands from it, then applying an offset to the command, and then combining all the commands to an SVG string. This is really painful.

@pwichmann
Copy link

Agreed.
I'll offer 3 coffee vouchers or so for someone to implement it. It would make my life a lot easier.

@Pomax
Copy link
Contributor Author

Pomax commented Apr 9, 2017

Alternatively, you can see if you can write a utility function that ingests the output of layout(), which is a GlyphRun object (which contains both the sequence of glyphs as well as a positions array with metrics) and outputs an SVG document, and then you could either try to slot that into fontkit itself as a PR against the TTFfont/LayoutEngine classes, or you could publish it as your own tiny dedicated fontkit-svg-generator library or the like. Especially if you find you keep "iterating through each glyph in the run, getting the commands from it, then applying an offset to the command, and then combining all the commands to an SVG string" that is clearly a thing you've already written code for, so that should be almost trivial to refactor into its own utility function that takes the output of font.layout(...), and can then be used to file a PR or publish an open source library that can then be iterated on until it's modular enough to suit everyone's needs while being concise enough not to bloat a codebase.

@DominikLevitsky
Copy link

This is a good idea, maybe I'll try doing this.

@randyabson
Copy link

Old issue. But did anyone end up finding/creating a solution for this? Struggling with this now...

@elron
Copy link

elron commented Mar 29, 2019

Created a solution for this, check it out: https://github.com/elron/Font-To-SVG

@jlarmstrongiv
Copy link

Has anyone made a solution that includes kerning support? I’d gladly donate to support this feature

@Pomax
Copy link
Contributor Author

Pomax commented Nov 3, 2021

for what purpose? Because if you just want fully typeset text, as SVG, opentype.js should be able to get you that no problem, with pretty much full opentype feature support (not just kerning)

@jlarmstrongiv
Copy link

It is similar to opentype.js, but I’m trying to support color fonts and additional font formats via fontkit.

I can already render single characters as svgs using bounding box calculations—works great. But, rendering kerning is more complex, and after looking into the opentype.js source, I think they use advance width instead of the bbox approach I’ve been using. While I could just hack away at it, I feel like there is more underlying complexity that I may be missing—such as the liga and rlig support you just mentioned.

Plus, this kind of workaround would be beneficial for fontkit, and has received interest from other community members too. I’d gladly donate to support examples and workarounds for this feature

@Pomax
Copy link
Contributor Author

Pomax commented Nov 4, 2021

Indeed. Not just kerning literally every opentype feature (GSUB/GPOS, liga, etc.) will won't if you're just combining based on hmtx/vmtx bounding box. You're basically guaranteed to always have the wrong output. At this point, what might be far more useful, and is a route I personally went down, is going "well forget about js, let XeLaTeX do the actual typesetting to PDF, then use PDF2SVG to turn that into the image data I need, because despite the fact that I've literally written more font parsers than I care to remember, XeLaTeX is still the gold standard, and available for literally every CI/CD platform you can imagine".

As for benefiting Fontkit: as far as I know, this code is no longer maintained, and the maintainer is not interested in marking it as such, so throwing money at OpenType.js, which very much is still being worked on, might be a better bet. Different holes, but at least a reasonable chance that going "I am willing to pay for someone to implement this" will actually get code landed.

@jlarmstrongiv
Copy link

jlarmstrongiv commented Nov 4, 2021

True, but support for color fonts is exceedingly rare, and is not completely supported in browsers, pdftosvg converters, latex, opentypejs, and other solutions I’ve tried. There’s really not many options to render them, especially as an svg.

For me, fontkit has pretty much everything I need except for creating a kerned svg. As for opentype features, I’m sure it’s a rabbit hole, but even opentype.js only supports "liga" and "rlig", so that’s good enough.

This package may be unmaintained for now, but it’s used by pdfkit and others, with a download count that keeps growing every day. I still think there’s plenty of features unique to fontkit, and I think having an example of toSVG(run); will be benefit many. I have already created a converter for individual letters, but stringing together words involves more expertise to deal with the underlying complexity.

If you’re up for the challenge, I will gladly donate several coffees for you to have a go at it. You were excellent with the OpenType SVG parser.

@devongovett
Copy link
Member

Pretty sure opentype.js doesn't support most complex text rendering features: https://rawgit.com/unicode-org/text-rendering-tests/master/reports/OpenType.js.html The last release of that library was also 2 years ago. So claiming "pretty much full opentype feature support" (I don't see any code for it in the repo), and that opentype.js is somehow better maintained seems incorrect.

@devongovett
Copy link
Member

As for your actual question, I don't think it would be very difficult. Basic steps:

  1. Call font.layout(). This returns a GlyphRun
  2. Create two variables, x and y, and initialize them to zero.
  3. Loop through each glyph. Retrieve the glyph from run.glyphs[index] and the position from run.positions[index]. The position is a GlyphPosition object.
  4. Call glyph.toSVG() on each glyph. Each glyph needs to be translated by the current x/y position, plus the position.xOffset/position.yOffset. To do this, wrap the returned SVG for the glyph in a group: <g transform="translate(${x + position.xOffset} ${y + position.yOffset})">${svg}</g>. Append this to the full SVG to be returned.
  5. Increment x and y by position.xAdvance and position.yAdvance respectively.
  6. Return the full concatenated SVG.

You can see a similar method to this that shows how these properties work here: https://github.com/foliojs/fontkit/blob/master/src/layout/GlyphRun.js#L89-L107 or in textkit: https://github.com/foliojs/textkit/blob/master/packages/pdf-renderer/index.js#L79-L90

@jlarmstrongiv
Copy link

jlarmstrongiv commented Nov 4, 2021

Thank you @devongovett !

Not really sure about the viewBox calculation of -height + -minY or .scale(-1, 1).rotate(Math.PI), do you or @Pomax know why that is or a better way to implement it? Otherwise 👍

Here’s the example code I put together:

const fs = require("fs-extra");
const fontkit = require("fontkit");
const times = require("lodash.times");
const svgo = require("svgo");

async function start() {
  const buffer = await fs.readFile("./public-sans.ttf");
  const font = fontkit.create(buffer);
  const run = font.layout("Hello World!");
  const bbox = run.bbox;

  let x = 0;
  let y = 0;
  const groups = times(run.glyphs.length, (index) => {
    const glyph = run.glyphs[index];
    const position = run.positions[index];

    const fill = "#000000";
    const fillOpacity = 1;
    const d = glyph.path.scale(-1, 1).rotate(Math.PI).toSVG();
    const path = `<path fill="${fill}" fill-opacity="${fillOpacity}" d="${d}" />`;

    const group = `<g transform="translate(${x + position.xOffset}, ${
      y + position.yOffset
    })">${path}</g>`;

    x += position.xAdvance;
    y += position.yAdvance;

    return group;
  });

  const minX = bbox.minX;
  const minY = bbox.minY;
  const width = bbox.maxX - bbox.minX;
  const height = bbox.maxY - bbox.minY;
  const svg = `
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="${minX} ${
    -height + -minY
  } ${width} ${height}">
    ${groups.join("\n")}
  </svg>
  `;

  const optimizedSvg = (await svgo.optimize(svg)).data;
  await fs.outputFile("word.svg", Buffer.from(optimizedSvg));
}

start();

@Pomax
Copy link
Contributor Author

Pomax commented Nov 4, 2021

Pretty sure opentype.js doesn't support most complex text rendering features: https://rawgit.com/unicode-org/text-rendering-tests/master/reports/OpenType.js.html The last release of that library was also 2 years ago. So claiming "pretty much full opentype feature support" (I don't see any code for it in the repo), and that opentype.js is somehow better maintained seems incorrect.

Fair enough. Last time I brought up that there were lots of open PRs and that there were quite a few issues looking for attention, your response was one that suggested you were not going to be working on fontkit anymore, so if that's changed: fantastic! Happy to help out if you need it.

@jlarmstrongiv
Copy link

jlarmstrongiv commented Feb 3, 2022

Hi @devongovett ! I’d like to extend the script I wrote (with your help!) to support multiple lines, max line length, and appropriate leading / line-height. Do you have an approach or examples that would help point me in the right direction?

@Typogram
Copy link

Typogram commented Aug 24, 2023

Thank you @devongovett !

Not really sure about the viewBox calculation of -height + -minY or .scale(-1, 1).rotate(Math.PI), do you or @Pomax know why that is or a better way to implement it? Otherwise 👍

Here’s the example code I put together:

const fs = require("fs-extra");
const fontkit = require("fontkit");
const times = require("lodash.times");
const svgo = require("svgo");

async function start() {
  const buffer = await fs.readFile("./public-sans.ttf");
  const font = fontkit.create(buffer);
  const run = font.layout("Hello World!");
  const bbox = run.bbox;

  let x = 0;
  let y = 0;
  const groups = times(run.glyphs.length, (index) => {
    const glyph = run.glyphs[index];
    const position = run.positions[index];

    const fill = "#000000";
    const fillOpacity = 1;
    const d = glyph.path.scale(-1, 1).rotate(Math.PI).toSVG();
    const path = `<path fill="${fill}" fill-opacity="${fillOpacity}" d="${d}" />`;

    const group = `<g transform="translate(${x + position.xOffset}, ${
      y + position.yOffset
    })">${path}</g>`;

    x += position.xAdvance;
    y += position.yAdvance;

    return group;
  });

  const minX = bbox.minX;
  const minY = bbox.minY;
  const width = bbox.maxX - bbox.minX;
  const height = bbox.maxY - bbox.minY;
  const svg = `
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="${minX} ${
    -height + -minY
  } ${width} ${height}">
    ${groups.join("\n")}
  </svg>
  `;

  const optimizedSvg = (await svgo.optimize(svg)).data;
  await fs.outputFile("word.svg", Buffer.from(optimizedSvg));
}

start();

Your code helped me create a solution in my fontkit demo! thank you!
I am considering switch from opentype.js to fontkit to support more opentype features

This is my code to replicate opentype.js font.getPath().toPathData() function:

function getPathData(run) {
  let pathData = '';
  let x = 0;
  let y = 0;
  run.glyphs.forEach((glyph, index) => {
    const position = run.positions[index];
    if (glyph.path) {
      pathData += glyph.path
        .scale(1, -1)
        .translate(x + position.xOffset, y + position.yOffset)
        .toSVG();
      x += position.xAdvance;
      y += position.yAdvance;
    }
  });
  return pathData;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants