Using @font-face with an SVG font embedded in the current HTML page

14,778

Solution 1

Convert the font to a data URI and embedded it in the CSS declaration: (fiddle)

<style>
@font-face {
  font-family: "My Font";
  src: url("data:application/octet-stream;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCIgPgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+CiAgPGZvbnQgaWQ9Ik15Rm9udEVsZW1lbnQiPgogICAgPGZvbnQtZmFjZSBmb250LWZhbWlseT0iTXlGb250IiAvPgogICAgPGdseXBoIHVuaWNvZGU9IkEiIGhvcml6LWFkdi14PSI5NTAiIGQ9Ik0wLDBMMCwxMDBMMTAwLDEwMEwxMDAsMjAwTDAsMjAwTDAsMzAwTDEwMCwzMDBMMTAwLDQwMEwyMDAsNDAwTDIwMCw1MDBMMzAwLDUwMEwzMDAsNjAwTDQwMCw2MDBMNjAwLDYwMEw2MDAsNTAwTDcwMCw1MDBMNzAwLDQwMEw4MDAsNDAwTDgwMCwzMDBMOTAwLDMwMEw5MDAsMjAwTDgwMCwyMDBMODAwLDEwMEw5MDAsMTAwTDkwMCwwTDYwMCwwTDYwMCwxMDBMNzAwLDEwMEw3MDAsMjAwTDgwMCwyMDBMODAwLDMwMEwxMDAsMzAwTDEwMCwyMDBMMjAwLDIwMEwyMDAsMTAwTDMwMCwxMDBMMzAwLDBMMCwwTTMwMCw0MDBMNjAwLDQwMEw2MDAsNTAwTDMwMCw1MDBMMzAwLDQwMFoiIC8+ICAgIAogIDwvZm9udD4KPC9kZWZzPjwvc3ZnPgo=") format("svg");
}
</style>
<p style="font: 3em 'My Font';">
    Alphabet
</p>

There's one caveat: you can't use an ID specifier (#MyFont) with a data URI like this. Therefore you can only have a single font in the encoded file, rather than having multiple and referring to them individually. (Not that you'd want to; duplicating the data for multiple embedded fonts in the declaration for each font would be a huge waste of space.)

Solution 2

Specify the local font name first in the css, then the embedded font name:

p {
    font-family: MyFontLocalName, MyFontEmbeddedName;
}

http://jsfiddle.net/gilly3/xX6Bv/5/

If the MyFontLocalName is installed on the user's computer, that font will be used, otherwise MyFontEmbeddedName will be used.

Share:
14,778
Jeremy
Author by

Jeremy

████ ████ ███████ ██ ███ ██████ █ ████ █ ███ ██ ████ ██ ██ ███ █ ████ ████ ███████ █ ███ █████ ██ █ ███ ████ ████ ███████ ███████ ███ ██ ██████ [mailto:[email protected]](https://mailto.jeremy.ca)

Updated on June 18, 2022

Comments

  • Jeremy
    Jeremy almost 2 years

    I have a standalone HTML document I want to distribute, without any external dependencies. I'm using a non-standard font in this document that only some of my users will have installed.

    For those users that do not have the font installed, I am including a copy of the font in an embedded SVG document at the top of the <body>. (I'm using a one-glyph font for this example, the real document is using a complete font.)

    <svg style="display: none;"><defs>
      <font id="MyFontElement">
        <font-face font-family="MyFont" />
        <glyph unicode="A" horiz-adv-x="950" d="M0,0L0,100L100,100L100,200L0,200L0,300L100,300L100,400L200,400L200,500L300,500L300,600L400,600L600,600L600,500L700,500L700,400L800,400L800,300L900,300L900,200L800,200L800,100L900,100L900,0L600,0L600,100L700,100L700,200L800,200L800,300L100,300L100,200L200,200L200,100L300,100L300,0L0,0M300,400L600,400L600,500L300,500L300,400Z" />    
      </font>
    </defs></svg>
    

    SVG fonts do not look as nice as ordinary fonts, so I only want the SVG font to be used if the font is not installed locally. If the font was defined in an external SVG document, I could use it at a lower priority than the locally-installed font like this: (fiddle)

    <style>
      @font-face {
        font-family: "My Font";
        src: local("My Font"), url("fonts.svg#MyFontElement") format("svg");
      }
    </style>
    <p style="font: 3em 'My Font';">
        Alphabet
    </p>
    

    screenshot

    Unfortunately, none of the obvious variations seem to work for a font in the current document: (fiddle)

      src: local("My Font"),
           url("./#MyFontElement") format("svg"),
           url("./#MyFontElement"),
           url("#MyFontElement") format("svg"),
           url("#MyFontElement");
    

    Even without a @font-face declaration, the font is already available in the document as MyFont, the font-family specified in the <font-face />. However, this will be used at a higher priority than a native font named MyFont, so it is not an solution.

    I hoped that I might be able to refer to it as local("MyFont")... (fiddle)

      src: local("My Font"), /* local */
           local("MyFont"); /* embedded */
    

    ...but that doesn't work either.

    I could give the embedded font a different name and use it at a lower priority, style="font-family: LocalFont, EmbeddedFont", but I'm allowing users to import snippets from local files into the document and those local files will refer to the font only by the standard name. It would be possible to rewrite these references when they're imported, but I don't like that solution.

    How do I refer to an SVG font embedded in the current document from a @font-face declaration?

  • Jeremy
    Jeremy almost 12 years
    This is a good suggestion, but it won't work for me. I'm sorry for not mentioning this in my post initially: I'm allowing users to import snippets from local documents that are referring to the font by its standard name, so this won't work for them. :( (I was referring to fonts by different names before, "X" and "X Embedded".)
  • Jeremy
    Jeremy almost 12 years
    Good idea, thanks, this work well! I've edited your post to include an example and a caveat I noted. However, I'm not going to accept this answer yet in case there is a solution that allows me to refer to a font in a normal embedded SVG document.
  • gilly3
    gilly3 almost 12 years
    @Jeremy - Tricky. I've actually thought about this some before. The best I've come up with would be an ugly hack and involve JavaScript: Put a piece of text into a span with the desired font and a fallback font at a very large size, Eg font-size: 200px; font-family: MyEmbeddableFont, Arial. Check the width of the span against the expected width of the test font. If the width is off, dynamically load the font.
  • Andy Davies
    Andy Davies almost 12 years
    Rather than using fragments wouldn't you just declare another font-face block with the other font?
  • Jeremy
    Jeremy almost 12 years
    Yup, that's what I'm going to do. It would be a bad decision to use fragments, but if it worked I might do it if I were feeling terribly lazy. I wanted to warn any other lazy readers that it won't work; it wasn't obvious to me.
  • Erik Dahlström
    Erik Dahlström almost 12 years
    You shouldn't use "application/octet-stream" if it's an svgfont, it should be "image/svg+xml". AFAIK you can use an ID specifier, at least I'm pretty sure that worked in Opera last I tried it. And you should always use an ID specifier with svgfonts, unless you are comfortable with depending on unspecified behavior. Here's a fixed up fiddle showing how it should look: jsfiddle.net/fPg3z
  • Jeremy
    Jeremy almost 12 years
    @ErikDahlström The WC3 working draft for CSS3 Fonts told me that "in the case of SVG fonts, the URL points to an element within a document containing SVG font definitions. If the element reference is omitted, a reference to the first defined font is implied". When I try to specify a fragment on the data URL, it breaks in Chrome. Oh dear...
  • Erik Dahlström
    Erik Dahlström almost 12 years
    Ah, hadn't seen that bit in the CSS3 Fonts draft, I'll file a bug for Opera to handle that case.