Why is my SVG file not using my font file?
Asked Answered
B

1

3

I have this SVG file in the same directory as this font's TTF file.

<?xml version="1.0" encoding="UTF-8"?>
<!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="150"
  height="100"
  viewBox="0 0 150 100"
>
  <style type="text/css">
    @font-face {
      font-family: "easyPrint";
      src: url('./easy-print.regular.ttf');
    }   
  </style>
  <text
    x="10"
    y="30"
    class="word"
    font-family="easyPrint"
    font-size="25"
  >
    TEST
  </text>
  <text
    x="10"
    y="80"
    font-size="25"
  >
    TEST
  </text>
</svg>

When I do this, I get two different fonts. The one that should be my custom font is the same font you see if you give it gibberish for a font-family. Which is different from the default.

I've tried with an OTF file as well. The specific font isn't important. I'm just trying to produce a bunch of SVG images of text with a specific font.

Bently answered 10/2, 2022 at 5:18 Comment(3)
I have just tried your code with random font from Google Fonts and it works. Do you have the correct path to the font file? Shouldn't it be ./easy-print-regular.ttf instead of ./easy-print.regular.ttf?Richela
How are you opening/viewing that SVG, stand-alone on its own, or is it embedded into an HTML page? What are browser console and network panel saying, any 404 or similar in there for the font request?Appomattox
I have the correct path, though that is a good thing to check. I am viewing this in ImageViewer, a Linux program that comes with Ubuntu. My goal for these images is to send many of them together in a zip file to a design company that will print them. So I think they need to be stand alone. Perhaps i need some process for converting them to PNG?Bently
J
6

If you need your svg files to be self contained,
you should consider embedding the font-files as base64 resource (using a tool like transfonter):

1. Example: font embedded as base64

This svg would display the custom font as long as your application supports CSS (inlined svg, referenced in img element, opened directly in browser).

<p>(Letter "a" is not contained in the reduced character subset)</p>

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 150 100">
<style>
@font-face {
    font-family: 'easyPrint';
     src: url('data:font/woff2;charset=utf-8;base64,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') format('woff2');
    font-weight: normal;
}
  </style>
  <text
    x="10"
    y="30"
    class="word"
    font-family="easyPrint"
    font-size="25"
  >
    TESTa
  </text>
</svg>

You can significantly reduce file size by subsetting your font:
Only selected characters will be available.

transfonter settings

If you're deploying google fonts you may also try vecta's nano optimizer which has a pretty need auto-embedding feature for publically available google fonts.

2. Example: Svg2Png

This approach requires the font to be inlined as dataURL.
Otherwise the png output will render a default font like Times.

function svg2Png(selector) {
  const svgEl = document.querySelector(selector);
  let svgVB = svgEl.getAttribute('viewBox');
  let svgVBArr = svgVB ? svgVB.split(' ') : '';
  let svgVBW = svgVBArr ? svgVBArr[2] : '';
  let svgVBH = svgVBArr ? svgVBArr[3] : '';

  let svgClientBB = svgEl.getBoundingClientRect();
  let svgClientW = svgClientBB.width;
  let svgClientH = svgClientBB.height;

  let svgBB = svgEl.getBBox();
  let svgW = svgVBW? svgVBW : svgBB.width;
  svgW = svgClientW > svgW ? svgClientW : svgW;
  let svgH = svgVBH? svgVBH : svgBB.height;
  svgH = svgClientH > svgH ? svgClientH : svgH;


  let blob = new Blob([svgEl.outerHTML], {type: 'image/svg+xml'});
  let URL = window.URL;
  let blobURL = URL.createObjectURL(blob);
  let tmpImg = new Image();
  tmpImg.src = blobURL;
  tmpImg.width = svgClientW > svgW ? svgClientW : svgW;
  tmpImg.height = svgClientH > svgH ? svgClientH : svgH;

  tmpImg.onload = () => {
    let canvas = document.createElement("canvas");
    canvas.width = svgW;
    canvas.height = svgH;
    let context = canvas.getContext("2d");
    
    // draw blob img to canvas with some delay
    setTimeout(function () {
      context.drawImage(tmpImg, 0, 0, svgW, svgH);
      let pngDataUrl = canvas.toDataURL();
      let svgImg = document.createElement("img");
      svgImg.width = svgW;
      svgImg.height = svgH;
      svgImg.class = "svgImg";
      svgImg.src = pngDataUrl;
      // just additional wrapping for example usage
      let imgWrp = document.createElement("div");
      imgWrp.setAttribute("class", "img-wrp");
      imgWrp.appendChild(svgImg);
      document.body.appendChild(imgWrp);
    }, 300);
  };
}
    svg,
        img{
            border:1px solid #ccc
        }

        .svg-wrp{
            position:relative;
            overflow:auto;
            resize:both;
            width:50%;

        }
<div class="btn-wrp">
  <button type="button" onclick="svg2Png('svg')">svg2Png</button>
</div>

<div class="svg-wrp">

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 150 100">
<style>
@font-face {
    font-family: 'easyPrint';
     src: url('data:font/woff2;charset=utf-8;base64,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') format('woff2');
    font-weight: normal;
}
  </style>
  <text
    x="10"
    y="30"
    class="word"
    font-family="easyPrint"
    font-size="25"
  >
    TEST
  </text>
</svg>

</div>

3. Example: create svg with text rendered as path

This example deploys the opentype.js library.
Opentype.js has a handy function font.getPath() allowing you to render string as a svg <path> element based on the before loaded font file.

let svgcontainer = document.querySelector('#svgcontainer');
let inputText = document.querySelector('#inputText');
// opentype.js accepts only ttf and otf
let fontFile = 'https://fonts.gstatic.com/s/firasans/v15/va9E4kDNxMZdWfMOD5Vvl4jO.ttf';
inputText.addEventListener('change', function(e) {
  let testString = e.currentTarget.value;
  let params = {
    string: testString,
    font: fontFile,
    fontSize: 100,
    x: 0,
    y: 0,
    decimals: 3
  }
  text2Path(params)
})

//default
let params = {
  string: inputText.value,
  font: fontFile,
  fontSize: 100,
  x: 0,
  y: 0,
  decimals: 3
}
text2Path(params)

function text2Path(params) {
  opentype.load(params.font, function(err, font) {
    if (!err) {
      let options = params.options;
      let path = font.getPath(params.string, params.x, params.fontSize, params.fontSize, options);
      let textPath = path.toSVG(params.decimals);
      let newSvg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
      newSvg.classList.add('svgText');
      newSvg.setAttribute('xmlns', "http://www.w3.org/2000/svg");
      newSvg.insertAdjacentHTML('beforeend', textPath);
      svgcontainer.appendChild(newSvg);

      // adjust bbox
      let bb = newSvg.getBBox();
      newSvg.setAttribute('viewBox', '0 0 ' + (bb.width + bb.x) + ' ' + (params.fontSize * 1.5));

      //add downloadbtn
      let btnDownload = document.createElement("a");
      btnDownload.setAttribute('download', 'font-' + params.string + '.svg');
      btnDownload.textContent = 'Download';
      let dataURl = getDataUrl(newSvg.outerHTML, 'image/svg+xml');
      btnDownload.href = dataURl;
      svgcontainer.appendChild(btnDownload)

    } else {
      console.log('Font could not be loaded: ' + err);
    }
  });
}

function getDataUrl(str, mime) {
  let dataUrl = 'data:' + mime + ';base64,' + btoa(unescape(encodeURIComponent(str)))
  return dataUrl;
}
.svgText {
  height: 10em;
  display: inline-block;
  border: 1px solid #ccc;
}

a {
  display: block
}
<script src="https://cdn.jsdelivr.net/npm/opentype.js@latest/dist/opentype.min.js"></script>

<div class="layout">
  <div class="frm-wrp">
    <input id="inputText" type="text" value="Test">
  </div>
  <div class="svgcontainer" id="svgcontainer"></div>
</div>

Alternative: convert text to path in graphic editor

This is probably the most convenient solution.

  • Make sure the desired font is installed locally (in you OS).
  • Open your svg in an graphic editor like inkscape.
  • select you <text> elements and convert them to paths:
    inkscape: Path / Object to path
    Adobe Illustrator: Type / Create Outlines

Font embedding in SVG

SVG won't automatically include/embed all used fonts! As mentioned before: Whether an application can handle embedded fonts heavily depends on its CSS support.
Most image viewers will fail.

Editing SVGs with embedded fonts

Usually, this won't work at all even if your using advanced editors like inkscape. Usually desktop applications rely on the system based font management so they can't access embedded font subsets in an editable mode.

You may also try to convert your svg to pdf within the browser and convert all fonts to path outlines when opening.
Inkskape (free and open source) has an option for this "Draw all text". (see also this post).

John answered 10/2, 2022 at 12:28 Comment(2)
This is pretty cool. It sadly also has the exact same behavior as my existing SVG. Which is that it does not work in ImageViewer but it does, in fact, work in Chrome. I checked with my original code and it also works just fine in Chrome. Leaving me unsure if it's going to work in whatever software the design company uses. But this is a handy want to pass the font along with the SVG in one file.Bently
@mmachenry: The new example will render a neat svg with converted contours (similar to Ais text to path). Just work pretty fine in any vector app.John

© 2022 - 2024 — McMap. All rights reserved.