Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 31 additions & 20 deletions src/embed-webfonts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,11 @@
async function getCSSRules(
styleSheets: CSSStyleSheet[],
options: Options,
): Promise<CSSStyleRule[]> {
const ret: CSSStyleRule[] = []
): Promise<CSSRule[]> {
const ret: CSSRule[] = []
const deferreds: Promise<number | void>[] = []

// First loop inlines imports
// replace @import rules with target stylesheet
styleSheets.forEach((sheet) => {
if ('cssRules' in sheet) {
try {
Expand Down Expand Up @@ -164,34 +164,45 @@
}
})

return Promise.all(deferreds).then(() => {
// Second loop parses rules
styleSheets.forEach((sheet) => {
if ('cssRules' in sheet) {
try {
toArray<CSSStyleRule>(sheet.cssRules || []).forEach((item) => {
ret.push(item)
})
} catch (e) {
console.error(`Error while reading CSS rules from ${sheet.href}`, e)
await Promise.all(deferreds)

const rulesCollections = toArray<{ cssRules: CSSRuleList }>(styleSheets)

while (rulesCollections.length) {
const rulesCollection = rulesCollections.pop()

if (!rulesCollection) {
continue
}

Check warning on line 176 in src/embed-webfonts.ts

View check run for this annotation

Codecov / codecov/patch

src/embed-webfonts.ts#L176

Added line #L176 was not covered by tests

try {
toArray<CSSRule>(rulesCollection.cssRules || []).forEach((rule) => {
ret.push(rule)

if ('cssRules' in rule) {
rulesCollections.unshift(rule as { cssRules: CSSRuleList })
}
}
})
})
} catch (e) {
console.error(`Error while reading CSS rules`, e)
}
}

return ret
})
return ret
}

function getWebFontRules(cssRules: CSSStyleRule[]): CSSStyleRule[] {
function getWebFontRules(cssRules: CSSRule[]): CSSFontFaceRule[] {
return cssRules
.filter((rule) => rule.type === CSSRule.FONT_FACE_RULE)
.filter(

Check warning on line 196 in src/embed-webfonts.ts

View check run for this annotation

Codecov / codecov/patch

src/embed-webfonts.ts#L196

Added line #L196 was not covered by tests
(rule): rule is CSSFontFaceRule => rule.type === CSSRule.FONT_FACE_RULE,
)
.filter((rule) => shouldEmbed(rule.style.getPropertyValue('src')))
}

async function parseWebFontRules<T extends HTMLElement>(
node: T,
options: Options,
) {
): Promise<CSSFontFaceRule[]> {
if (node.ownerDocument == null) {
throw new Error('Provided element is not within a Document')
}
Expand Down
65 changes: 56 additions & 9 deletions test/spec/webfont.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ describe('font embedding', () => {
try {
root.innerHTML = `
<style>
@font-face {
@font-face {
font-family: 'Font 0';
src: url('https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu72xKKTU1Kvnz.woff2');
}
@font-face {
@font-face {
font-family: 'Font 1';
src: url('https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu72xKKTU1Kvnz.woff2');
}
@font-face {
@font-face {
font-family: 'Font 2';
src: url('https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu72xKKTU1Kvnz.woff2');
}
Expand All @@ -40,15 +40,15 @@ describe('font embedding', () => {
try {
root.innerHTML = `
<style>
@font-face {
@font-face {
font-family: 'Font 0';
src: url('https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu72xKKTU1Kvnz.woff2');
}
@font-face {
@font-face {
font-family: 'Font 1';
src: url('https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu72xKKTU1Kvnz.woff2');
}
@font-face {
@font-face {
font-family: 'Font 2';
src: url('https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu72xKKTU1Kvnz.woff2');
}
Expand All @@ -72,15 +72,15 @@ describe('font embedding', () => {
try {
root.innerHTML = `
<style>
@font-face {
@font-face {
font-family: 'Font 0';
src: url('https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu72xKKTU1Kvnz.woff2');
}
@font-face {
@font-face {
font-family: 'Font 1';
src: url('https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu72xKKTU1Kvnz.woff2');
}
@font-face {
@font-face {
font-family: 'Font 2';
src: url('https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu72xKKTU1Kvnz.woff2');
}
Expand All @@ -103,5 +103,52 @@ describe('font embedding', () => {
root.remove()
}
})
it('should embed font defined in CSS grouping rule', async () => {
const root = document.createElement('div')
document.body.append(root)
try {
root.innerHTML = `
<style>
@layer layer1;

@layer layer1 {
@font-face {
font-family: 'Font 0';
src: url('https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu72xKKTU1Kvnz.woff2');
}
}

@media screen {
@font-face {
font-family: 'Font 1';
src: url('https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu72xKKTU1Kvnz.woff2');
}
}

@font-face {
font-family: 'Font 2';
src: url('https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu72xKKTU1Kvnz.woff2');
}
</style>
<p style="font-family: 'Font 0'">Hello world</p>
<p style="font-family: 'Font 1'">Hello world</p>
`
const svg = await htmlToImage.toSvg(root)
const doc = await getSvgDocument(svg)
const [style] = Array.from(doc.getElementsByTagName('style'))

expect(style.textContent).toContain(
'font-family: "Font 0"; src: url("data:application/font-woff;base64,',
)
expect(style.textContent).toContain(
'font-family: "Font 1"; src: url("data:application/font-woff;base64,',
)
expect(style.textContent).not.toContain(
'font-family: "Font 2"; src: url("data:application/font-woff;base64,',
)
} finally {
root.remove()
}
})
})
})
Loading