How do I get glyph outlines of a letter as bézier paths using JavaScript?

后端 未结 3 628
温柔的废话
温柔的废话 2020-12-05 22:09

I want to retrieve the outline information of font glyphs as bézier paths in HTML5. This would allow me to randomize the outlines:

相关标签:
3条回答
  • 2020-12-05 22:29

    Low-level parsing

    In JavaScript you will have to manually parse the typeface file and extract the path data you need. AFAIK there is no helper-library for this at the moment so you are left to do this yourselves.

    To parse a file you will also need to upload it manually and parse it as raw data using the new File API and possible typed arrays for binary font formats (ttf).

    It's possible to write a very long answer for this as this is rather low-level stuff and to broad to be covered here (IMHO). However, here are the file format specifications for the common font files:

    • WOFF (recommended)
    • Embedded OpenType
    • Open type
    • Open Font
    • TrueType
    • Type 1 (PDF)

    But there is another approach you can take as an intermediate step saving you a lot of pain:

    Pre-parse

    Select the typefaces you want to use. Run them through Java/Cocoa/.NET (WPF) or whatever you prefer and build arrays containing the information you need (for example, WPF gives you the path data for each glyph). Convert them to JavaScript arrays or JSON objects and use those for the render and manipulation. The resulting points will be in em values which are easy to scale.

    Now you can simply go through the arrays and render the path segment type (line, bezier etc.) and modify the point positions.

    mozPathText is for canvas and just holds the text path (currently Path API is not exposed in any browser, but will in the future - however, it cannot be used for this as the low-level path data per glyph isn't available for many reason, legal reasons possibly being one).

    Trace

    The third approach is to sort of "OCR" the glyphs on the canvas by tracing each individual glyph and try to build paths from it's traced outline.

    This can be done in several way - one way is to parse line by line and group pixels based on distance/connectivity and then iterate through the group/region using neural networks or fill algorithms to find the outer boundaries for the glyph.

    Or - you can draw the text as outlines and just trace the connected pixels for each shape and use that as a line path.

    In this case you may run into challenges that may seem simple enough but can turn out to not be.

    Example: the letter O consist of an outer path and an inner path. The inner path punches a "hole" in the outer path. You cannot define this behavior directly just by defining paths in canvas so you need to add "cuts" between the two paths after you have determined they belong to each other, and then join them at the cuts.

    Using different composite modes may help in creating the illusion of such but you will still need to determine which path is the solid part and which will be drawn as the "hole".

    Warp/distortion

    A final approach is to not use glyph paths at all but just warp the bitmap using interpolation and warp grid (typically used in connection with 3D textures):

    http://davis.wpi.edu/~matt/courses/morph/2d.htm
    Warping (pdf)
    Morphing (pdf)

    Define your own fonts/paths (manual trace)

    The more obvious option perhaps: define your own font paths from scratch. A bit more work in the area of either making a font designer or by manually tracing an image, or defining the paths manually. A "font-plotter" (using a trace image) is not so complicated but require patience.

    You can use a canvas in the browser to record the lines you are plotting and use smoothing algorithms (cardinal spline + knee detection) to get nice round curves and sharp corners.

    It all depends on what result you want in the end and how much work you are willing to put down. There is unfortunately no free ticket in this regard as the internal data isn't exposed (and probably won't be in the future either).

    0 讨论(0)
  • 2020-12-05 22:31

    With SVG it's possible in some browsers to do the reverse of what you're asking: specify custom font glyphs as bezier paths, and then lay out text using them. You could possibly import an SVG font and then perform your transformations on that. But even though SVG is now widely supported, SVG fonts are not. Only Safari, Chrome, and Opera currently have support for them.

    If you want to do these types of transformations with system fonts in a vector way, that's not possible in any browser that I know of. There's simply no API in HTML or SVG to get the outline path of an installed glyph. (Maybe there's something you can use in a plugin like Flash, I haven't checked out their text API for a while.)

    If you're okay doing it the raster way, you can use a hidden canvas element to lay out your text in an off screen buffer and then get a pixel map of the result. You can then perform pixel-based transformations on it and draw that into a second, visible canvas element.

    0 讨论(0)
  • 2020-12-05 22:43

    Out of necessity I've created my own library called opentype.js. It parses OpenType and TrueType fonts. PostScript and WOFF will be supported in the future.

    Here's how it parses a typeface:

    • Load the .ttf / .otf file using a XMLHttpRequest.
    • Parse the glyf and loca table to extract the letter shapes (glyphs).
    • Parse the cname table which contains the mapping from characters to glyphs.
    • Parse the head and hmtx table to get the metrics, basically the spacing between each letter.

    Then it can create a bézier path:

    • Convert the letters of the text into glyphs.
    • Convert the coordinates of the glyph to quadratic curves.
    • Adjust the spacing using kerning information.

    This results in a path that you can draw using the HTML5 canvas:

    var font = opentype.parseFont(arrayBuffer);
    var path = font.getPath("Hello, World!", {x:0, y:150, fontSize:72});
    path.draw(ctx);
    

    Demo of opentype.js

    The demo website has a live example.

    0 讨论(0)
提交回复
热议问题