javascriptphpsvgopentypemaker-js

opentype.js and maker.js rendering incorrect path for text


I need to be able to convert text and a specific font to svg path data, and happened to come across opentype.js and maker.js, independent of each other, seeing that maker.js uses opentype.js. When I use opentype.js by itself, it doesn't render the font path correctly... when I give it a fill it doesn't display it properly. When I use maker.js, it is significantly better, but still incorrect.

TO BE ABSOLUTELY CLEAR ON THE PROBLEM DESCRIPTION:

I need a way to translate text with a given font to SVG path data that is then "flattened" (any black overlaps are unioned and any elements that are inside of the unioned text that is white is kept - for instance look at the letter "P"... If I union it, it removes the white part inside of the "P", of which I need). At this point I don't care about the means... it just needs to be compatible with php (it can't use Imagemagick/Inkscape or other third-party software ran from the terminal. I don't have access to run shell_exec() or exec()) or JavaScript.

---maker.js---

This is one attempt... running this snippet causes my browser to crash, but in its own file works fine.

Here is the result and the code:

maker.js

window.onload = function()
{

    var makerjs = require('makerjs');

    var url = 'https://staging-gumcreekboards.temp312.kinsta.cloud/wp-content/themes/Divi-child/CanvEsIntegration/static/CreekWareFonts/woff/work_in_progress-updated.woff';
    var text = 'Shane';
    var size = 72;
    var union = true;
    var bezierAccuracy = 0;
    var svg = document.createElement('div');
    opentype.load(url, (err, font) => {
        textModel = new makerjs.models.Text(font, text, size, union, true, bezierAccuracy);
        //console.log(textModel.models, makerjs.model.simplify);
        svg.innerHTML = makerjs.exporter.toSVG((textModel));
        document.getElementById('svgcontainer').innerHTML = svg.innerHTML;
    });

};
<html>
<head>

  <script src="https://cdn.jsdelivr.net/npm/makerjs@0/target/js/browser.maker.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/bezier-js@2/bezier.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/opentype.js@0/dist/opentype.js"></script>

</head>
<body>

<style>path {stroke: red; stroke-width: 1px; stroke-linejoin: round; fill: black !important;}</style>

<div id="svgcontainer">
</div>

</body>
</html>

---opentype.js---

This one won't run on SO, but it works fine on the console on the website I am programming on.

Here is the result and the code:

opentype.js

svgD = "";

font = await opentype.load("https://staging-gumcreekboards.temp312.kinsta.cloud/wp-content/themes/Divi-child/CanvEsIntegration/static/CreekWareFonts/woff/ARRUS BT BOLD.ttf");

pathsArray = font.getPaths("Shane", 0, 0, 72);
svgDArray = [];
svgPaths = "";

for (var i = 0; i < pathsArray.length; i++)
{
    svgDArray = [];
    svgD = "";
    path = pathsArray[i].commands;
    for (var j = 0; j < path.length; j++)
    {
            for (each in path[j])
            {
                svgDArray.push(path[j][each])
            }
            svgD += svgDArray.join(" ") + " ";
    }

    if (svgD.length > 0)
    {
        svgPaths += "<path fill='black' d='" + svgD + "' />";
    }
    
};

document.getElementById("svgcontainer").innerHTML = "<?xml version='1.0' encoding='UTF-8' standalone='no'?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3/org/1999/xlink' version='1.1' width='665' height='554' viewBox='0 0 665 554' xml:space='preserve'><style>path {stroke: red; stroke-width: 1px; stroke-linejoin: 'round'; fill: black;}</style><g transform='matrix(0.95 0 0 0.95 300 275)'>" + svgPaths + "</g></svg>";
<html>
<head>

<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/opentype.js@latest/dist/opentype.min.js"></script>

</head>
<body>

<div id="svgcontainer">
</div>

</body>
</html>


Solution

  • The culprit is a faulty font definition. In , the character a is shown as having intersecting paths. If you look at the path data output of opentype.js for this glyph, it looks like this (formatted for reading convenience):

    M 127.872 27.864 L 128.088 27.864 L 128.304 27.864 Z
    M 147.240 48.240
    C 143.568 54.288 133.200 71.856 128.736 71.856
    C 127.800 71.856 126.432 70.992 126.432 67.896
    C 126.432 59.472 135.504 48.960 135.504 44.496
    C 135.504 39.744 132.984 39.168 131.760 39.168
    C 130.680 35.856 127.152 36.432 127.152 37.080
    C 127.152 37.944 129.816 37.440 129.816 41.112
    C 129.816 44.352 126.504 49.680 123.696 55.152
    C 121.392 58.608 113.040 70.848 108.504 70.848
    C 94.176 70.848 120.168 32.472 132.120 32.472
    C 135.144 32.472 134.208 34.488 136.152 34.488
    C 137.880 34.488 136.368 30.312 134.208 30.312
    C 130.896 30.312 131.256 27.936 128.088 27.864
    C 115.704 28.152 97.056 51.912 97.056 59.040
    C 97.056 64.872 101.304 66.096 101.304 66.096
    C 101.304 66.096 102.024 74.016 108.216 74.016
    C 111.744 74.016 116.496 70.200 120.024 66.888
    C 120.312 70.632 123.264 71.424 123.264 71.424
    C 123.264 71.424 123.048 75.312 127.584 75.312
    C 134.928 75.312 145.872 55.728 149.688 49.248
    C 150.768 47.520 148.464 46.440 147.240 48.240Z
    

    The first line represents a closed line going from one point to a second, and then back. It coincides with part of the following path and somehow leads to an error in the maker.js processing.

    I have ripped apart your script to get at the underlying data for each glyph, and then patched in path data for the letter a that are shortened by the first line.

    var makerjs = require('makerjs');
    var opentype = require('opentype.js');
    
    // does this really work with a https: url?
    // I had to download the font first.
    var url = './work_in_progress.woff';
    var text = 'Shane';
    var size = 72;
    var union = true;
    var bezierAccuracy = 0;
    var decimalPlaces = 3;
    
    var patch = 'M147.240 48.240C143.568 54.288 133.200 71.856 128.736 71.856C127.800 71.856 126.432 70.992 126.432 67.896C126.432 59.472 135.504 48.960 135.504 44.496C135.504 39.744 132.984 39.168 131.760 39.168C130.680 35.856 127.152 36.432 127.152 37.080C127.152 37.944 129.816 37.440 129.816 41.112C129.816 44.352 126.504 49.680 123.696 55.152C121.392 58.608 113.040 70.848 108.504 70.848C94.176 70.848 120.168 32.472 132.120 32.472C135.144 32.472 134.208 34.488 136.152 34.488C137.880 34.488 136.368 30.312 134.208 30.312C130.896 30.312 131.256 27.936 128.088 27.864C115.704 28.152 97.056 51.912 97.056 59.040C97.056 64.872 101.304 66.096 101.304 66.096C101.304 66.096 102.024 74.016 108.216 74.016C111.744 74.016 116.496 70.200 120.024 66.888C120.312 70.632 123.264 71.424 123.264 71.424C123.264 71.424 123.048 75.312 127.584 75.312C134.928 75.312 145.872 55.728 149.688 49.248C150.768 47.520 148.464 46.440 147.240 48.240Z'
    
    var advance = 0;
    
    opentype.load(url, function (err, font) {
       var pathData = font.stringToGlyphs(text).map((glyph, i) => {
          var data = glyph.name === 'a' ? patch : glyph.getPath(advance, size, size).toPathData(decimalPlaces);
          advance += glyph.advanceWidth / font.unitsPerEm * size;
          return data;
       });
       
       var model = makerjs.importer.fromSVGPathData(pathData.shift());
       while (pathData.length) {
          model = makerjs.model.combineUnion(model, makerjs.importer.fromSVGPathData(pathData.shift()));
       }
    
       console.log(makerjs.exporter.toSVG(model))
    });
    

    The result is an error-free SVG:

    <svg width="238.831" height="80.64" viewBox="0 0 238.831 80.64" xmlns="http://www.w3.org/2000/svg">
      <g id="svgGroup" stroke-linecap="round" fill-rule="evenodd" font-size="9pt" stroke="#000" stroke-width="0.25mm" fill="none" style="stroke:#000;stroke-width:0.25mm;fill:none">
        <path d="M 81.583 4.583 A 2.808 2.808 0 0 0 81.518 4.527 C 80.945 4.046 80.372 3.969 80.302 3.961 A 0.334 0.334 0 0 0 80.294 3.96 A 0.813 0.813 0 0 0 80.302 3.936 C 80.397 3.606 81.304 0 75.326 0 C 65.24 0 57.331 12.114 49.896 26.51 A 353.269 353.269 0 0 0 47.534 31.176 A 97.596 97.596 0 0 1 36.625 34.453 C 32.357 35.459 28.088 36.127 23.99 36.35 A 54.403 54.403 0 0 1 21.038 36.432 A 27.275 27.275 0 0 1 19.52 36.388 A 65.736 65.736 0 0 1 18.086 36.288 A 38.497 38.497 0 0 1 24.743 28.637 C 27.038 26.669 29.488 25.209 32.025 24.463 A 13.332 13.332 0 0 1 35.798 23.904 C 39.9 23.904 38.727 26.291 40.841 26.606 A 3.4 3.4 0 0 0 41.342 26.64 A 1.296 1.296 0 0 0 41.835 26.553 C 43.47 25.885 41.539 21.6 38.75 21.6 A 11.364 11.364 0 0 1 37.863 21.568 C 34.439 21.299 36.221 19.41 32.489 19.237 A 12.51 12.51 0 0 0 31.91 19.224 C 25.66 19.224 18.975 24.325 13.363 31.182 A 64.608 64.608 0 0 0 10.886 34.416 A 14.476 14.476 0 0 1 9.133 33.413 C 3.936 29.928 3.717 24.61 3.271 24.482 A 0.059 0.059 0 0 0 3.254 24.48 A 4.237 4.237 0 0 0 2.05 24.641 C -0.978 25.536 -0.308 29.936 1.861 31.532 A 2.817 2.817 0 0 0 3.542 32.112 A 2.318 2.318 0 0 0 3.525 32.166 C 3.338 32.766 1.805 38.21 6.494 41.472 A 73.166 73.166 0 0 0 6.1 42.216 C 3.549 47.093 1.706 51.971 0.959 56.098 A 21.133 21.133 0 0 0 0.59 59.832 A 30.02 30.02 0 0 0 0.739 62.929 C 1.487 70.123 4.91 71.375 7.621 71.745 A 16.086 16.086 0 0 0 7.934 71.784 C 7.934 71.784 7.358 78.408 14.342 78.408 A 29.02 29.02 0 0 0 36.122 66.959 C 42.263 59.974 47.32 50.612 51.81 41.25 A 532.357 532.357 0 0 0 55.67 32.976 C 70.934 25.128 82.742 14.76 82.742 7.848 A 7.138 7.138 0 0 0 82.742 7.801 A 3.548 3.548 0 0 1 82.754 7.852 C 82.805 8.072 82.843 8.317 82.865 8.588 A 6.944 6.944 0 0 1 82.886 9.144 C 82.886 20.664 57.542 53.784 57.542 62.856 A 7.45 7.45 0 0 0 57.606 63.851 C 58.006 66.814 60.134 67.464 60.134 67.464 A 1.717 1.717 0 0 0 60.138 67.553 C 60.17 68.115 60.522 71.178 64.341 71.345 A 7.558 7.558 0 0 0 64.67 71.352 C 67.262 71.352 68.414 68.904 67.406 68.904 A 14.36 14.36 0 0 1 66.523 68.885 C 65.193 68.804 63.809 68.363 63.673 65.986 A 6.91 6.91 0 0 1 63.662 65.592 A 7.853 7.853 0 0 1 63.847 64.013 C 64.159 62.515 64.859 60.638 65.894 58.464 A 360.462 360.462 0 0 1 67.044 56.988 C 72.953 49.446 89.438 28.964 89.438 34.92 A 6.897 6.897 0 0 1 88.824 37.417 C 86.282 43.625 76.591 54.788 73.922 64.826 A 18.756 18.756 0 0 0 73.238 69.624 A 9.91 9.91 0 0 0 73.51 72.021 C 74.184 74.729 76.037 76.139 78.206 76.824 A 0.306 0.306 0 0 0 78.206 76.826 C 78.203 76.932 78.135 80.389 82.217 80.627 A 7.78 7.78 0 0 0 82.67 80.64 A 6.521 6.521 0 0 0 85.605 79.781 C 90.346 77.31 96.594 69.885 102.014 62.46 A 224.182 224.182 0 0 0 105.838 57.042 A 8.505 8.505 0 0 0 106.159 58.362 C 107.197 61.483 109.697 62.342 109.939 62.418 A 0.869 0.869 0 0 0 109.958 62.424 A 4.717 4.717 0 0 0 109.974 62.561 C 110.093 63.523 110.988 69.233 115.505 70.204 A 6.485 6.485 0 0 0 116.87 70.344 A 6.888 6.888 0 0 0 119.059 69.939 C 122.208 68.873 125.839 65.881 128.678 63.216 A 6.576 6.576 0 0 0 128.839 64.253 C 129.528 67.111 131.918 67.752 131.918 67.752 C 131.918 67.752 131.702 71.64 136.238 71.64 A 5.955 5.955 0 0 0 138.907 70.908 C 141.734 69.471 144.854 66.075 147.799 62.144 A 100.486 100.486 0 0 0 150.941 57.682 A 5.911 5.911 0 0 0 150.976 57.804 C 151.637 59.971 153.302 60.48 153.302 60.48 A 1.717 1.717 0 0 0 153.306 60.569 C 153.338 61.131 153.69 64.194 157.509 64.361 A 7.558 7.558 0 0 0 157.838 64.368 C 160.343 64.368 161.571 62.081 160.675 61.928 A 0.598 0.598 0 0 0 160.574 61.92 A 14.36 14.36 0 0 1 159.691 61.901 C 158.361 61.82 156.977 61.379 156.841 59.002 A 6.91 6.91 0 0 1 156.83 58.608 A 6.358 6.358 0 0 1 157.261 56.554 C 158.201 53.906 160.539 50.266 162.878 46.368 C 166.982 41.184 182.606 21.528 182.606 27.864 A 6.897 6.897 0 0 1 181.992 30.361 C 179.45 36.569 169.759 47.732 167.09 57.77 A 18.756 18.756 0 0 0 166.406 62.568 A 9.854 9.854 0 0 0 166.668 64.917 C 167.334 67.633 169.194 69.008 171.374 69.696 A 0.317 0.317 0 0 0 171.374 69.698 C 171.371 69.806 171.303 73.346 175.416 73.573 A 7.675 7.675 0 0 0 175.838 73.584 A 7.514 7.514 0 0 0 178.977 72.791 C 183.506 70.68 188.746 64.674 193.157 58.668 A 146.1 146.1 0 0 0 194.138 57.313 A 8.514 8.514 0 0 0 194.126 57.744 A 12.778 12.778 0 0 0 194.21 59.242 C 194.829 64.482 198.676 64.718 198.936 64.728 A 0.674 0.674 0 0 0 198.95 64.728 C 199.526 68.04 201.542 71.136 207.014 71.136 A 27.831 27.831 0 0 0 225.82 62.434 C 231.78 56.784 236.03 49.813 238.173 46.152 A 260.69 260.69 0 0 0 238.55 45.504 A 2.07 2.07 0 0 0 238.7 45.235 C 239.342 43.831 237.483 43.086 236.418 44.338 A 2.26 2.26 0 0 0 236.246 44.568 A 90.188 90.188 0 0 0 236.178 44.677 C 233.12 49.585 225.832 62.241 214.726 66.236 A 20.319 20.319 0 0 1 207.806 67.464 A 7.651 7.651 0 0 1 205.542 67.157 C 200.749 65.673 201.708 59.008 205.07 51.84 C 206.006 52.992 207.446 53.784 209.75 53.784 A 10.278 10.278 0 0 0 212.716 53.308 C 224.053 49.875 238.091 29.026 225.664 28.944 A 11.178 11.178 0 0 0 225.59 28.944 A 1.81 1.81 0 0 1 224.995 28.853 C 223.42 28.309 224.04 25.508 219.934 25.286 A 9.918 9.918 0 0 0 219.398 25.272 C 214.723 25.272 205.898 34.401 200.014 43.529 A 58.66 58.66 0 0 0 199.556 44.245 A 2.045 2.045 0 0 0 199.519 44.287 A 2.447 2.447 0 0 0 199.31 44.568 A 196.187 196.187 0 0 1 198.106 46.487 C 193.368 53.946 182.433 70.128 176.99 70.128 C 174.038 70.128 173.246 67.032 173.246 63.432 A 13.819 13.819 0 0 1 173.883 59.594 C 176.665 50.143 187.693 36.489 189.487 29.891 A 6.192 6.192 0 0 0 189.734 28.296 A 4.404 4.404 0 0 0 189.682 27.572 C 189.516 26.588 188.95 26.232 187.836 26.209 A 5.781 5.781 0 0 0 187.718 26.208 C 185.274 26.208 185.629 24.27 183.265 24.264 A 4.291 4.291 0 0 0 183.254 24.264 A 6.585 6.585 0 0 0 180.36 25.056 C 176.283 27.09 171.477 32.715 167.27 38.016 A 21.187 21.187 0 0 0 168.47 34.288 A 13.221 13.221 0 0 0 168.782 31.536 C 168.782 26.784 166.262 26.208 165.038 26.208 A 4.534 4.534 0 0 0 164.693 25.409 C 163.354 23 160.43 23.53 160.43 24.12 A 0.428 0.428 0 0 0 160.639 24.472 C 161.266 24.934 163.094 25.11 163.094 28.152 A 13.069 13.069 0 0 1 162.432 31.989 C 161.357 35.441 159.13 39.265 156.902 42.93 A 242.794 242.794 0 0 0 155.921 44.529 A 2.619 2.619 0 0 0 155.894 44.568 C 152.585 50.019 143.837 64.827 138.873 67.698 A 3.066 3.066 0 0 1 137.39 68.184 A 1.64 1.64 0 0 1 136.923 68.114 C 136.137 67.88 135.265 67.015 135.11 64.907 A 9.349 9.349 0 0 1 135.086 64.224 C 135.086 56.101 143.521 46.038 144.124 41.326 A 3.959 3.959 0 0 0 144.158 40.824 C 144.158 36.072 141.638 35.496 140.414 35.496 A 4.534 4.534 0 0 0 140.069 34.697 C 138.73 32.288 135.806 32.818 135.806 33.408 A 0.428 0.428 0 0 0 136.015 33.76 C 136.642 34.222 138.47 34.398 138.47 37.44 A 8.389 8.389 0 0 1 138.016 39.907 C 137.184 42.533 135.332 45.845 133.48 49.324 A 161.941 161.941 0 0 0 132.35 51.48 A 140.561 140.561 0 0 1 131.575 52.632 C 128.667 56.902 121.827 66.415 117.642 67.133 A 2.862 2.862 0 0 1 117.158 67.176 C 102.83 67.176 128.822 28.8 140.774 28.8 A 5.464 5.464 0 0 1 141.399 28.833 C 143.553 29.082 143.072 30.639 144.543 30.802 A 2.393 2.393 0 0 0 144.806 30.816 A 0.768 0.768 0 0 0 145.18 30.73 C 146.271 30.133 144.838 26.64 142.862 26.64 C 139.55 26.64 139.91 24.264 136.742 24.192 A 13.214 13.214 0 0 0 132.62 25.028 C 124.268 28.023 114.516 38.447 109.352 46.661 A 39.056 39.056 0 0 0 108.252 48.497 A 285.983 285.983 0 0 1 107.789 49.208 C 101.471 58.891 88.769 77.184 83.75 77.184 A 3.1 3.1 0 0 1 80.812 75.215 C 80.5 74.563 80.293 73.782 80.166 72.921 A 16.901 16.901 0 0 1 80.006 70.488 A 13.764 13.764 0 0 1 80.646 66.65 C 83.44 57.199 94.516 43.545 96.318 36.947 A 6.167 6.167 0 0 0 96.566 35.352 A 4.322 4.322 0 0 0 96.511 34.614 C 96.345 33.662 95.791 33.304 94.679 33.267 A 6.094 6.094 0 0 0 94.478 33.264 A 3.658 3.658 0 0 1 93.853 33.214 C 92.146 32.918 92.158 31.414 90.06 31.392 A 4.451 4.451 0 0 0 90.014 31.392 C 85.04 31.392 79.351 37.678 74.367 43.963 A 309.445 309.445 0 0 0 71.654 47.448 A 1606.937 1606.937 0 0 1 75.841 40.216 C 81.544 30.385 87.285 20.272 88.386 14.356 A 10.095 10.095 0 0 0 88.574 12.528 C 88.574 7.776 86.054 7.2 84.83 7.2 C 84.269 5.477 83.045 4.787 81.999 4.623 A 3.32 3.32 0 0 0 81.583 4.583 Z M 13.838 43.776 A 63.154 63.154 0 0 0 11.698 48.786 C 6.969 61.329 6.88 73.872 15.134 73.872 A 11.627 11.627 0 0 0 19.486 73.008 C 28.86 69.257 36.451 53.872 44.15 38.16 C 38.822 40.158 33.374 41.771 28.084 42.776 A 60.525 60.525 0 0 1 16.862 43.92 C 15.782 43.92 14.774 43.848 13.838 43.776 Z M 61.773 19.929 A 264.186 264.186 0 0 0 58.694 26.352 A 93.957 93.957 0 0 0 61.185 25.045 C 71.698 19.323 79.142 12.578 79.142 7.632 A 10.427 10.427 0 0 0 79.121 6.944 C 79.006 5.201 78.404 4.39 77.045 4.13 A 6.525 6.525 0 0 0 75.83 4.032 A 7.135 7.135 0 0 0 71.542 5.643 C 68.202 8.144 65.049 13.364 61.773 19.929 Z M 225.249 31.219 A 2.554 2.554 0 0 0 224.222 31.032 A 4.263 4.263 0 0 0 223.893 31.045 C 221 31.269 217.135 34.376 213.475 38.703 A 60.739 60.739 0 0 0 207.158 47.808 A 3.611 3.611 0 0 0 209.181 49.701 A 3.683 3.683 0 0 0 210.398 49.896 C 218.667 49.896 229.743 33.181 225.249 31.219 Z" vector-effect="non-scaling-stroke"/>
      </g>
    </svg>