Home > Back-end >  Text is not converting to right svg code - Opentype Javascript
Text is not converting to right svg code - Opentype Javascript

Time:06-06

I am trying to convert text to svg in my React web app, but the resulted svg code which i am getting is not right, t's not showing svg.
Code:

import opentype from "opentype.js"

async function textToPath(){
    const text = "Hello"
    const size = {x: 50, y: 25}
    const fontFile = "https://fonts.gstatic.com/s/firasans/v15/va9E4kDNxMZdWfMOD5Vvl4jO.ttf"
    let params = {
        string: text,
        font: fontFile,
        fontSize: size.x,
        decimals: 1,
        singleGylyphs: false
    }
    const font = await opentype.load(params.font);

    let options = params.options;
    let unitsPerEm = font.unitsPerEm;
    let ratio = params.fontSize / unitsPerEm;
    let ascender = font.ascender;
    let descender = Math.abs(font.descender);
    let ratAsc = ascender / unitsPerEm;
    let ratDesc = descender / unitsPerEm;
    let yOffset = params.fontSize * ratAsc;
    let lineHeight = params.fontSize   params.fontSize * ratDesc;
    let singleGylyphs = params.singleGylyphs;

    let teststring = params.string.split("");

    let glyphs = font.stringToGlyphs(params.string);
    let leftSB = glyphs[0].leftSideBearing * ratio;
    let textPath = "";

    //individual paths for each glyph
    if (singleGylyphs) {
        let paths = font.getPaths(
            params.string,
            -leftSB,
            yOffset,
            params.fontSize,
            options
        );
        paths.forEach(function (path, i) {
            let pathEl = path.toSVG(params.decimals);
            textPath  = pathEl.replaceAll(
                "d=",
                ' d='
            );
        });
    }
    //word (all glyphs) merged to one path
    else {
        let path = font.getPath(
            params.string,
            -leftSB,
            yOffset,
            params.fontSize,
            options
        );
        textPath  = path
            .toSVG(params.decimals)
            .replaceAll("d=", ' d=');
    }

    // render
    let fontSvgWrp = document.createElement("div");
    fontSvgWrp.classList.add("fontSvgWrp");
    let fontSvg = document.createElementNS(
        "http://www.w3.org/2000/svg",
        "svg"
    );
    fontSvg.classList.add("svgText");
    fontSvg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
    fontSvg.innerHTML = textPath;
    fontSvgWrp.appendChild(fontSvg);

    // adjust bbox
    let bb = fontSvg.getBBox();
    let stringWidth = Math.ceil(bb.width   bb.x);
    fontSvg.setAttribute("viewBox", "0 0 "   stringWidth   " "   lineHeight);
    fontSvg.setAttribute("width", size.x);
    fontSvg.setAttribute("data-asc", ratAsc);
    console.log(fontSvg); // getting svg code

    let textPathSvg = fontSvg.querySelector(".glyph");
    let textPathLength = textPathSvg.getTotalLength();
    
    return textPathLength
}

It's generating this svg which is not working:

<svg  xmlns="http://www.w3.org/2000/svg" viewBox="0 0 0 5013.25" width="50" data-asc="0.935"><path  d="M24 46.8L19.3 46.8L19.3 30.6L4.8 30.6L4.8 46.8L0 46.8L0 12.3L4.8 12.3L4.8 26.7L19.3 26.7L19.3 12.3L24 12.3L24 46.8ZM53.7 32.8Q53.7 34.0 53.5 35.1L53.5 35.1L36.8 35.1Q37.0 39.5 39.0 41.5Q40.9 43.6 43.9 43.6L43.9 43.6Q45.8 43.6 47.4 43.0Q49.0 42.5 50.7 41.3L50.7 41.3L52.7 44.0Q48.5 47.4 43.5 47.4L43.5 47.4Q38 47.4 34.9 43.8Q31.9 40.1 31.9 33.9L31.9 33.9Q31.9 29.8 33.2 26.6Q34.5 23.4 37.0 21.6Q39.5 19.8 42.8 19.8L42.8 19.8Q48.0 19.8 50.9 23.3Q53.7 26.7 53.7 32.8L53.7 32.8ZM49.1 31.8L49.1 31.4Q49.1 27.5 47.5 25.5Q46 23.4 42.9 23.4L42.9 23.4Q37.3 23.4 36.8 31.8L36.8 31.8L49.1 31.8ZM66.3 47.4Q63.7 47.4 62.2 45.8Q60.8 44.3 60.8 41.5L60.8 41.5L60.8 9.8L65.3 9.3L65.3 41.5Q65.3 42.5 65.7 43.0Q66.1 43.5 67 43.5L67 43.5Q68.0 43.5 68.7 43.3L68.7 43.3L69.9 46.5Q68.3 47.4 66.3 47.4L66.3 47.4ZM80.9 47.4Q78.4 47.4 76.9 45.8Q75.4 44.3 75.4 41.5L75.4 41.5L75.4 9.8L80 9.3L80 41.5Q80 42.5 80.4 43.0Q80.8 43.5 81.7 43.5L81.7 43.5Q82.6 43.5 83.4 43.3L83.4 43.3L84.6 46.5Q82.9 47.4 80.9 47.4L80.9 47.4ZM100.2 19.8Q105.8 19.8 108.8 23.5Q111.9 27.2 111.9 33.5L111.9 33.5Q111.9 37.6 110.5 40.8Q109.1 43.9 106.5 45.6Q103.8 47.4 100.2 47.4L100.2 47.4Q94.6 47.4 91.5 43.6Q88.4 40.0 88.4 33.6L88.4 33.6Q88.4 29.5 89.8 26.4Q91.2 23.3 93.9 21.5Q96.5 19.8 100.2 19.8L100.2 19.8ZM100.2 23.5Q93.4 23.5 93.4 33.6L93.4 33.6Q93.4 43.6 100.2 43.6L100.2 43.6Q107.0 43.6 107.0 33.5L107.0 33.5Q107.0 23.5 100.2 23.5L100.2 23.5Z"></path></svg>

I tried & searched a lot but i didn't get anything to fix this thing and why the converted svg is not working. Can someone help?

CodePudding user response:

The svg output is actually OK but your viewBox isn't calculated correctly.

The viewBox width value is 0 – so your path is completely cropped/invisible.

The main problem in your script: you need to append the svg to your DOM otherwise getBBox() will return an empty object (or width:0, height:0).

It should rather look like this:

fontSvgWrp.appendChild(fontSvg);
document.body.appendChild(fontSvgWrp);
// adjust bbox
let bb = fontSvg.getBBox();

Example: appending svg before getBBox()

let fontSVG = textToPath();

async function textToPath(){
    const text = "Hello"
    const size = {x: 50, y: 25}
    const fontFile = "https://fonts.gstatic.com/s/firasans/v15/va9E4kDNxMZdWfMOD5Vvl4jO.ttf"
    let params = {
        string: text,
        font: fontFile,
        fontSize: size.x,
        decimals: 1,
        singleGylyphs: false
    }
    const font = await opentype.load(params.font);

    let options = params.options;
    let unitsPerEm = font.unitsPerEm;
    let ratio = params.fontSize / unitsPerEm;
    let ascender = font.ascender;
    let descender = Math.abs(font.descender);
    let ratAsc = ascender / unitsPerEm;
    let ratDesc = descender / unitsPerEm;
    let yOffset = params.fontSize * ratAsc;
    let lineHeight = params.fontSize   params.fontSize * ratDesc;
    let singleGylyphs = params.singleGylyphs;

    let teststring = params.string.split("");

    let glyphs = font.stringToGlyphs(params.string);
    let leftSB = glyphs[0].leftSideBearing * ratio;
    let textPath = "";

    //individual paths for each glyph
    if (singleGylyphs) {
        let paths = font.getPaths(
            params.string,
            -leftSB,
            yOffset,
            params.fontSize,
            options
        );
        paths.forEach(function (path, i) {
            let pathEl = path.toSVG(params.decimals);
            textPath  = pathEl.replaceAll(
                "d=",
                ' d='
            );
        });
    }
    //word (all glyphs) merged to one path
    else {
        let path = font.getPath(
            params.string,
            -leftSB,
            yOffset,
            params.fontSize,
            options
        );
        textPath  = path
            .toSVG(params.decimals)
            .replaceAll("d=", ' d=');
    }

    // render
    let fontSvgWrp = document.createElement("div");
    fontSvgWrp.classList.add("fontSvgWrp");
    let fontSvg = document.createElementNS(
        "http://www.w3.org/2000/svg",
        "svg"
    );
    fontSvg.classList.add("svgText");
    fontSvg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
    fontSvg.innerHTML = textPath;
    fontSvgWrp.appendChild(fontSvg);
    document.body.appendChild(fontSvgWrp);

    // adjust bbox
    let bb = fontSvg.getBBox();
  //console.log(bb)
    let stringWidth = Math.ceil(bb.width   bb.x);
    //let stringWidth = Math.ceil(bb.width);
    fontSvg.setAttribute("viewBox", "0 0 "   stringWidth   " "   lineHeight);
    //fontSvg.setAttribute("width", size.x);
    fontSvg.setAttribute("data-asc", ratAsc);
    console.log(fontSvg); // getting svg code
  
    let textPathSvg = fontSvg.querySelector(".glyph");
    let textPathLength = textPathSvg.getTotalLength();
    
    return textPathLength
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/opentype.js/1.3.4/opentype.min.js"></script>

Alternative: calculate total width from glyph data

If you don't want to append the svg you could also calculate the viewBox width using the glyphs advanceWidth data:

  //calculate viewBox width by glyphs' advanceWidths
  let totalWidth = 0;
  let lastGlyph = glyphs[glyphs.length-1];
  let lastSideBearing = (lastGlyph.leftSideBearing) * ratio;
  glyphs.forEach(function (glyph, i) {
    let advanceWidth = glyph.advanceWidth;
    totalWidth  = advanceWidth * ratio;
  });

  let stringWidth = Math.ceil(totalWidth - leftSB - lastSideBearing);

let fontSVG = textToPath();

async function textToPath() {
  const text = "Hello";
  const size = { x: 50, y: 25 };
  const fontFile =
    "https://fonts.gstatic.com/s/firasans/v15/va9E4kDNxMZdWfMOD5Vvl4jO.ttf";
  let params = {
    string: text,
    font: fontFile,
    fontSize: size.x,
    decimals: 1,
    singleGylyphs: false
  };
  const font = await opentype.load(params.font);

  let options = params.options;
  let unitsPerEm = font.unitsPerEm;
  let ratio = params.fontSize / unitsPerEm;
  let ascender = font.ascender;
  let descender = Math.abs(font.descender);
  let ratAsc = ascender / unitsPerEm;
  let ratDesc = descender / unitsPerEm;
  let yOffset = params.fontSize * ratAsc;
  let lineHeight = params.fontSize   params.fontSize * ratDesc;
  let singleGylyphs = params.singleGylyphs;

  let teststring = params.string.split("");

  let glyphs = font.stringToGlyphs(params.string);
  let leftSB = glyphs[0].leftSideBearing * ratio;
  let textPath = "";

  //individual paths for each glyph
  if (singleGylyphs) {
    let paths = font.getPaths(
      params.string,
      -leftSB,
      yOffset,
      params.fontSize,
      options
    );
    paths.forEach(function (path, i) {
      let pathEl = path.toSVG(params.decimals);
      textPath  = pathEl.replaceAll(
        "d=",
        ' d='
      );
    });
  }
  //word (all glyphs) merged to one path
  else {
    let path = font.getPath(
      params.string,
      -leftSB,
      yOffset,
      params.fontSize,
      options
    );
    textPath  = path
      .toSVG(params.decimals)
      .replaceAll("d=", ' d=');
  }

  // render
  let fontSvgWrp = document.createElement("div");
  fontSvgWrp.classList.add("fontSvgWrp");
  let fontSvg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
  fontSvg.classList.add("svgText");
  fontSvg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
  fontSvg.innerHTML = textPath;
  fontSvgWrp.appendChild(fontSvg);

  //calculate viewBox width by glyphs' advanceWidths
  let totalWidth = 0;
  let lastGlyph = glyphs[glyphs.length-1];
  let lastSideBearing = (lastGlyph.leftSideBearing) * ratio;
  glyphs.forEach(function (glyph, i) {
    let advanceWidth = glyph.advanceWidth;
    totalWidth  = advanceWidth * ratio;
  });

  let stringWidth = Math.ceil(totalWidth - leftSB - lastSideBearing);

  // adjust bbox
  let bb = fontSvg.getBBox();
  //console.log(bb)
  //let stringWidth = Math.ceil(bb.width   bb.x);
  //let stringWidth = Math.ceil(bb.width);
  fontSvg.setAttribute("viewBox", "0 0 "   stringWidth   " "   lineHeight);
  //fontSvg.setAttribute("width", size.x);
  fontSvg.setAttribute("data-asc", ratAsc);
  //console.log(fontSvg); // getting svg code

  let textPathSvg = fontSvg.querySelector(".glyph");
  let textPathLength = textPathSvg.getTotalLength();
  
  document.body.appendChild(fontSvgWrp);

  return textPathLength;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/opentype.js/1.3.4/opentype.min.js"></script>

  • Related