Skip to content

Commit

Permalink
added embedFonts option
Browse files Browse the repository at this point in the history
  • Loading branch information
0xb4lint committed Nov 13, 2023
1 parent 52724e0 commit 10cc061
Show file tree
Hide file tree
Showing 10 changed files with 396 additions and 105 deletions.
184 changes: 92 additions & 92 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "vite-plugin-webfont-dl",
"version": "3.8.1",
"version": "3.9.0",
"description": "Vite plugin for downloading and injecting webfonts",
"keywords": [
"vite",
Expand Down
3 changes: 3 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ To make it work with [Laravel Vite Plugin](https://laravel.com/docs/vite) add th
- **`minifyCss`** <small>(`boolean`, default: *value of* `build.minify`)</small>:<br>
Minify CSS code during build.

- **`embedFonts`** <small>(`boolean`, default: `false`)</small>:<br>
Embed base64-encoded fonts into css.

- **`async`** <small>(`boolean`, default: `true`)</small>:<br>
Prevent the usage of inline event handlers (`webfonts.css`) that can cause Content Security Policy issues.<br>
Works only with **`injectAsStyleTag:false`**.
Expand Down
37 changes: 32 additions & 5 deletions src/css-transformer.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,44 @@
import type { FontCollection } from './types';
import type { Font, FontCollection, FontExtension, Options } from './types';

enum FontMime {
woff2 = 'font/woff2',
woff = 'font/woff',
ttf = 'font/ttf',
otf = 'font/otf',
svg = 'image/svg+xml',
eot = 'application/vnd.ms-fontobject',
}

export class CssTransformer {
constructor(
private options: Options,
) {}

public transform(
cssContent: string,
fonts: FontCollection
): string {
for (const fontFile in fonts) {
const font = fonts[fontFile];
fonts.forEach((font: Font) => {
if (!this.options.embedFonts || !font.binary) {
cssContent = cssContent.replaceAll(font.url, font.localPath);
} else if (font.binary) {
const fontUrlRegex = new RegExp(`url\\(['"]?\\b${font.url}\\b['"]?\\)`, 'gi');

cssContent = cssContent.replaceAll(
fontUrlRegex,
`url(data:${this.getFontMime(font)};base64,${font.binary.toString('base64')})`
);
}
});

cssContent = cssContent.replaceAll(font.url, font.localPath);
}

return cssContent;
}

private getFontMime(font: Font): string {
const extension = font.filename.replace(/^.+\.(.+)$/, '$1') as FontExtension;

return FontMime[extension];
}
}

1 change: 1 addition & 0 deletions src/default-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { Options } from './types';
const defaultOptions: Options = {
injectAsStyleTag: true,
minifyCss: true,
embedFonts: false,
async: true,
cache: true,
proxy: false,
Expand Down
12 changes: 8 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,10 +194,14 @@ function viteWebfontDownload(
};

const saveFont = (font: Font, binary: Buffer) => {
font.localPath = base + saveFile(
font.filename,
binary
);
if (!options.embedFonts) {
font.localPath = base + saveFile(
font.filename,
binary
);
} else {
font.binary = binary;
}
};

const loadAndPrepareDevFonts = async () => {
Expand Down
8 changes: 8 additions & 0 deletions src/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { AxiosProxyConfig } from 'axios';

export type FontExtension = 'woff2' | 'woff' | 'ttf' | 'otf' | 'svg' | 'eot';

export interface Font {
url: string;
filename: string;
Expand Down Expand Up @@ -28,6 +30,12 @@ export interface Options {
* */
minifyCss?: boolean;

/**
* Embed base64-encoded fonts into css.
* default: `false`
* */
embedFonts?: boolean;

/**
* Load stylesheet asynchronously (using `media="print"`).
* Works only with `injectAsStyleTag:false`).
Expand Down
2 changes: 0 additions & 2 deletions test/css-parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ describe('css parser', () => {
const css = readFileSync(__dirname + '/fixtures/google-fonts-kit.css').toString();
const fonts = (new CssParser()).parse(css, '/', 'assets');

console.log(fonts);

expect(fonts.size).eq(1);
});

Expand Down
18 changes: 17 additions & 1 deletion test/css-transformer.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { readFileSync } from 'fs';
import { CssParser } from 'src/css-parser';
import { CssTransformer } from 'src/css-transformer';
import { Font } from 'src/types';
import { describe, expect, it } from 'vitest';

describe('css transformer', () => {
Expand All @@ -10,7 +11,22 @@ describe('css transformer', () => {
const cssExpected = readFileSync(__dirname + '/fixtures/google-fonts-transformed.css').toString();

const fonts = (new CssParser()).parse(cssBefore, '/', 'assets');
const cssAfter = (new CssTransformer()).transform(cssBefore, fonts);
const cssAfter = (new CssTransformer({})).transform(cssBefore, fonts);

expect(cssAfter).eq(cssExpected);
});

it('should embed fonts', () => {
const cssBefore = readFileSync(__dirname + '/fixtures/google-fonts.css').toString();
const cssExpected = readFileSync(__dirname + '/fixtures/google-fonts-embedded.css').toString();

const fonts = (new CssParser()).parse(cssBefore, '/', 'assets');

fonts.forEach((font: Font) => {
font.binary = Buffer.from('TEST');
});

const cssAfter = (new CssTransformer({ embedFonts: true })).transform(cssBefore, fonts);

expect(cssAfter).eq(cssExpected);
});
Expand Down
234 changes: 234 additions & 0 deletions test/fixtures/google-fonts-embedded.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
/* cyrillic-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url(data:font/woff2;base64,VEVTVA==) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url(data:font/woff2;base64,VEVTVA==) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url(data:font/woff2;base64,VEVTVA==) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url(data:font/woff2;base64,VEVTVA==) format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url(data:font/woff2;base64,VEVTVA==) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url(data:font/woff2;base64,VEVTVA==) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url(data:font/woff2;base64,VEVTVA==) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(data:font/woff2;base64,VEVTVA==) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(data:font/woff2;base64,VEVTVA==) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(data:font/woff2;base64,VEVTVA==) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(data:font/woff2;base64,VEVTVA==) format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(data:font/woff2;base64,VEVTVA==) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(data:font/woff2;base64,VEVTVA==) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(data:font/woff2;base64,VEVTVA==) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Roboto Mono';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url(data:font/woff2;base64,VEVTVA==) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Roboto Mono';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url(data:font/woff2;base64,VEVTVA==) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek */
@font-face {
font-family: 'Roboto Mono';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url(data:font/woff2;base64,VEVTVA==) format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Roboto Mono';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url(data:font/woff2;base64,VEVTVA==) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Roboto Mono';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url(data:font/woff2;base64,VEVTVA==) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Roboto Mono';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url(data:font/woff2;base64,VEVTVA==) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Roboto Mono';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(data:font/woff2;base64,VEVTVA==) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Roboto Mono';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(data:font/woff2;base64,VEVTVA==) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek */
@font-face {
font-family: 'Roboto Mono';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(data:font/woff2;base64,VEVTVA==) format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Roboto Mono';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(data:font/woff2;base64,VEVTVA==) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Roboto Mono';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(data:font/woff2;base64,VEVTVA==) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Roboto Mono';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(data:font/woff2;base64,VEVTVA==) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

0 comments on commit 10cc061

Please sign in to comment.