Search code examples
angularweb-componentcustom-font

Angular web component bundle external custom fonts package


I have created a web component using custom elements with Angular 8. Part of the component is to show some custom icons (developed and packaged as a different app). I have included the package of the custom icons in the package.json file of the component:

"dependencies": {
     "@somelibrary/icons": "1.0.34"
}

I gets correctly installed in the node modules. In the angular.json file I include the css file in the styles:

"styles": [
    "src/styles.css",
    "node_modules/@somelibrary/icons/css/fontface.css"
],

Where the fontface.css file specifies the urls of the custom font files with font-face:

@font-face {
  font-family: "my-fonts";
  src:url('../fonts/my-fonts.woff2') format('woff2'),
    url('../fonts/my-fonts.woff') format('woff'),
    url('../fonts/my-fonts.ttf') format('truetype');
  font-weight: normal;
  font-style: normal;
}

I then build the custom web component and concatenate all created js files into one, to be used as a standalone component.

cat dist/lib/runtime.js dist/lib/polyfills.js dist/lib/scripts.js dist/lib/main.js > dist/myComponent.js

The issue here is that when I use the final produced javascript file (include it in a simple index.html page), the tff and woff files are not included, so I end up just seeing the placeholder of the icon.

I have tried including the font files during the built myComponent.js file but as expected I get a syntax error.

cat dist/lib/runtime.js dist/lib/polyfills.js dist/lib/scripts.js dist/lib/main.js dist/lib/my-fonts.ttf> dist/myComponent.js

I have seen answers like this but the goal is a portable component and also because the fonts are external I cannot move them to the assets folder as they may change over time.

I have also explored options from this thread, I cannot externalize assets (since they come from a package) and the url-loader used for ttf and wof files does not work.

Has anyone ever tried something similar? How can a web component composed of a single javascript file include external fonts that are shown in a standalone application.


Solution

  • I finally managed to solve my issue.

    Through package.json and before build I encode the .woff file (containing the icons) to base64. Then create a new fontface-copy.css where the src url includes all data within a Data URI like this:

    src:url(`data:application/octet-stream;base64,[encoded_output]`)
    

    Finally include the newly created file in the main css file and after build the icons are bundled inside the javascript file.

    Update with more details after request:

    The way I handled it was inside package.json file, in the build script and before ng build I added some additional npm run commands to include the fonts file in the bundled js.

    First step is to encode the woff file containing the fonts to base64 using the following command

    base64 my-fonts.woff > base64-fonts.txt 
    

    Next step is to add the string data:application/octet-stream;base64, in front of the encoded text. This can be done using the sed command.

    In the application there should exist a .css file (with font-face) that links to the .woff file like this:

    @font-face {
      font-family: "my-fonts";
      src:url('../fonts/my-fonts.woff') format('woff');
      font-weight: normal;
      font-style: normal;
    }
    

    Last step is to replace the url link inside the .css file with the contents of the base64-fonts.txt file just created (keeping the quotations, using sed as well). The new css file now includes inline the encoded fonts and once the application is built, the fonts should be included in the bundled file.

    A small example of the package.json file:

    "build": "npm run fonts-encode && npm run fonts-edit && npm run replace-css && ng build my-app --prod --output-hashing=none",
    "fonts-encode": "base64 my-fonts.woff > base64-fonts.txt",
    "fonts-edit": "[sed_command] base64-fonts.txt",
    "replace-css": "[sed_command] base64-fonts.txt font-face.css"