Skip to content

Commit a19c81a

Browse files
authored
Merge 86cf7ba into e1af55e
2 parents e1af55e + 86cf7ba commit a19c81a

9 files changed

Lines changed: 440 additions & 108 deletions

File tree

‎packages/global-styles-ui/src/font-library/upload-fonts.tsx‎

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { FontLibraryContext } from './context';
1616
import { Font } from './lib/lib-font.browser';
1717
import makeFamiliesFromFaces from './utils/make-families-from-faces';
1818
import { loadFontFaceInBrowser } from './utils';
19+
import { createCssString } from './utils/create-css-string';
1920

2021
function UploadFonts() {
2122
const { installFonts } = useContext( FontLibraryContext );
@@ -101,15 +102,28 @@ function UploadFonts() {
101102
const fontFacesLoaded = await Promise.all(
102103
files.map( async ( fontFile: File ) => {
103104
const fontFaceData = await getFontFaceMetadata( fontFile );
104-
await loadFontFaceInBrowser(
105-
fontFaceData,
106-
fontFaceData.file,
107-
'all'
108-
);
105+
// It's technically possible to use "" as a font's family name, but
106+
// that's disallowed here.
107+
if ( fontFaceData.fontFamily ) {
108+
loadFontFaceInBrowser(
109+
{
110+
...fontFaceData,
111+
fontFamily: createCssString(
112+
fontFaceData.fontFamily
113+
),
114+
},
115+
fontFaceData.file,
116+
'all'
117+
);
118+
}
109119
return fontFaceData;
110120
} )
111121
);
112-
handleInstall( fontFacesLoaded );
122+
handleInstall(
123+
fontFacesLoaded.filter(
124+
( fontFaceData ) => fontFaceData.fontFamily
125+
)
126+
);
113127
};
114128

115129
/**
@@ -151,8 +165,10 @@ function UploadFonts() {
151165
);
152166
const font = onloadEvent.detail.font;
153167
const { name } = font.opentype.tables;
154-
const fontName = name.get( 16 ) || name.get( 1 );
155-
const isItalic = name.get( 2 ).toLowerCase().includes( 'italic' );
168+
const fontName = ( name.get( 16 ) || name.get( 1 ) || '' ).trim();
169+
const isItalic = ( name.get( 2 ) || '' )
170+
.toLowerCase()
171+
.includes( 'italic' );
156172
const fontWeight =
157173
font.opentype.tables[ 'OS/2' ].usWeightClass || 'normal';
158174
const isVariable = !! font.opentype.tables.fvar;
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/**
2+
* Construct a quoted CSS String from a plain JavaScript value.
3+
*
4+
* A plain JavaScript string will be transformed to a quoted CSS string suitable for interpolation
5+
* in CSS text.
6+
*
7+
* The input is not trimmed. Trim should be performed before calling this method, if desired.
8+
*
9+
* @example
10+
* ```js
11+
* const fontName = 'CSS & a "<style>" tag\'s strings';
12+
* const fontFaceRule = `@font-face { font-family: ${createCssString(fontName)}; }`
13+
* ```
14+
*
15+
* `fontFaceRule` will have the value `'@font-face { font-family: "CSS \\26 a \\22 \\3C style\\3E \\22 tag\\27 s strings"; }'`.
16+
* The browser will parse this rule as equivalent to: `@font-face { font-family: "CSS & a \"<style>\" tag's strings"; }`.
17+
*
18+
* Some characters that are valid in CSS strings are escaped to avoid problems with subsequent
19+
* processing that may not correctly parse CSS:
20+
*
21+
* - "<", ">", and "&" are replaced to prevent issues with KSES and other sanitization that
22+
* is confused by HTML-like text.
23+
* - CSS syntax characters are replaced to prevent issues where CSS may be processed by simple
24+
* string splits or search and replaces: "',;{}
25+
* - Plain "\" backslashes are escaped.
26+
*
27+
* @param value The JavaScript string to serialize as a quoted CSS string.
28+
* @return A quoted CSS string suitable for interpolation in CSS text.
29+
*/
30+
export function createCssString( value: string ): string {
31+
return `"${ value
32+
/*
33+
* CSS Unicode escaping for problematic characters.
34+
* https://www.w3.org/TR/css-syntax-3/#escaping
35+
*
36+
* Note that the Unicode escape sequences are used rather than backslash-escaping so the
37+
* problematic characters are removed completely. CSS Unicode escapes are formed by a
38+
* "\" followed by the character code in hexadecimal. The escape may be terminated by
39+
* whitespace which is ignored.
40+
*/
41+
42+
// Escape existing backslashes to prevent unintentional escapes in result.
43+
.replaceAll( '\\', '\\5C ' )
44+
45+
// Pre-processing replaces NULLs and some newlines. Replace and escape as necessary.
46+
.replaceAll( '\0', '\uFFFD' )
47+
48+
// Normalize and replace newlines. https://www.w3.org/TR/css-syntax-3/#input-preprocessing
49+
.replaceAll( '\r\n', '\\A ' )
50+
.replaceAll( '\r', '\\A ' )
51+
.replaceAll( '\f', '\\A ' )
52+
53+
// Newlines must be escaped in CSS strings.
54+
.replaceAll( '\n', '\\A ' )
55+
56+
// Arbitrary characters for Unicode escaping:
57+
58+
// HTML syntax may be problematic.
59+
.replaceAll( '<', '\\3C ' )
60+
.replaceAll( '>', '\\3E ' )
61+
.replaceAll( '&', '\\26 ' )
62+
63+
// CSS syntax may be problematic.
64+
.replaceAll( ',', '\\2C ' )
65+
.replaceAll( ';', '\\3B ' )
66+
.replaceAll( '{', '\\7B ' )
67+
.replaceAll( '}', '\\7D ' )
68+
.replaceAll( '"', '\\22 ' )
69+
.replaceAll( "'", '\\27 ' ) }"`;
70+
}

0 commit comments

Comments
 (0)