@@ -8,15 +8,19 @@ const SYMBOL =
88 / ^ ( c m ( m i | s y | e x | b s y | m i b ) | m s a m | m s b m | e u ( f m | f b | r m | s m | e x ) | r s f s | s t m a r y | w a s y | l a s y | e s i n t | b b m | d s r o m | t x s y | t x m i | t x e x | p x s y | p x m i | p x e x | m t s y | m t m i | m t e x ) | m a t h | s y m b o l | d i n g b a t | w i n g d i n g | w e b d i n g | z a p f | m a r v o s y m | f o n t a w e s o m e | e s s t i x | e u c l i d / ;
99const MONOSPACE = / m o n o | c o u r i e r | c o n s o l | m e n l o | ^ c m t t | ^ s f t t | ^ e c t t | i n c o n s o l a t a | t y p e w r i t e r | c o d e / ;
1010const SANS =
11- / s a n s | g r o t e s k | g r o t e s q u e | g o t h i c | a r i a l | h e l v e t i c a | v e r d a n a | c a l i b r i | s e g o e | t a h o m a | t r e b u c h e t | m y r i a d | f r u t i g e r | f u t u r a | g i l l s a n s | u n i v e r s | a v e n i r | r o b o t o | l a t o | u b u n t u | ^ c m s s | ^ s f s s | ^ l m s s | n i m b u s s a n | y a h e i | s i m h e i | h e i t i | p i n g f a n g | h i r a g i n o k a k u | m e i r y o | g u l i m | d o t u m | m a l g u n | f r a n k l i n | o p t i m a | c a n d a r a | c o r b e l | l u c i d a g r a n d e | s f p r o | s f n s | a p t o s / ;
11+ / s a n s | g r o t e s k | g r o t e s q u e | g o t h i c | a r i a l | h e l v e t i c a | v e r d a n a | c a l i b r i | s e g o e | t a h o m a | t r e b u c h e t | m y r i a d | f r u t i g e r | f u t u r a | g i l l s a n s | u n i v e r s | a v e n i r | r o b o t o | l a t o | u b u n t u | ^ c m s s | ^ s f s s | ^ l m s s | n i m b u s s a n | y a h e i | s i m h e i | h e i t i | h e i $ | d e n g x i a n | p i n g f a n g | h i r a g i n o k a k u | m e i r y o | g u l i m | d o t u m | m a l g u n | f r a n k l i n | o p t i m a | c a n d a r a | c o r b e l | l u c i d a g r a n d e | s f p r o | s f n s | a p t o s / ;
1212const SERIF =
13- / s e r i f | t i m e s | r o m a n | g e o r g i a | g a r a m o n d | m i n i o n | p a l a t i n o | a n t i q u a | b a s k e r v i l l e | c a s l o n | c a m b r i a | c h a r t e r | c e n t u r y | l i b e r t i n | b o d o n i | d i d o t | u t o p i a | m e r c u r y | w a r n o c k | s a b o n | b e m b o | j a n s o n | c o c h i n | h o e f l e r | c o n s t a n t i a | m e r r i w e a t h e r | l o r a | t i e m p o s | l i t e r a t a | s p e c t r a l | c r i m s o n | s t i x | n i m b u s r o m | ^ c m ( r | b x | t i | s l | c s c | b | u | b x t i | b x s l ) \d | ^ s f ( r m | b x | t i | s l | c c ) \d | ^ e c ( r m | b x | t i | s l | c c | b i ) | ^ l m r | ^ t x r | ^ p x r | m i n c h o | s o n g | m i n g | b a t a n g | m y u n g j o | c h a r i s | g e n t i u m | j o a n n a | p e r p e t u a | p l a n t i n | e l e c t r a | n e w s r e a d e r | i o w a n | s i t k a / ;
13+ / s e r i f | t i m e s | r o m a n | g e o r g i a | g a r a m o n d | m i n i o n | p a l a t i n o | a n t i q u a | b a s k e r v i l l e | c a s l o n | c a m b r i a | c h a r t e r | c e n t u r y | l i b e r t i n | b o d o n i | d i d o t | u t o p i a | m e r c u r y | w a r n o c k | s a b o n | b e m b o | j a n s o n | c o c h i n | h o e f l e r | c o n s t a n t i a | m e r r i w e a t h e r | l o r a | t i e m p o s | l i t e r a t a | s p e c t r a l | c r i m s o n | s t i x | n i m b u s r o m | ^ c m ( r | b x | t i | s l | c s c | b | u | b x t i | b x s l ) \d | ^ s f ( r m | b x | t i | s l | c c ) \d | ^ e c ( r m | b x | t i | s l | c c | b i ) | ^ l m r | ^ t x r | ^ p x r | m i n c h o | s o n g | s i m s u n | m i n g | k a i | b a t a n g | m y u n g j o | c h a r i s | g e n t i u m | j o a n n a | p e r p e t u a | p l a n t i n | e l e c t r a | n e w s r e a d e r | i o w a n | s i t k a / ;
1414const BOLD = / b o l d | b l a c k | h e a v y | d e m i | [ - , ] m e d i ( i t a l ) ? $ | ^ c m b x | ^ c m b \d | ^ s f b x | ^ e c b x | ^ c m s s b x / ;
1515const ITALIC = / i t a l | o b l i q | [ - , ] [ a - z ] * i t $ | ^ c m ( t i | s l | b x t i | b x s l | s s i ) | ^ s f ( t i | s l ) | ^ e c ( t i | s l | b i ) / ;
1616// Text that a local face can stand in for: letters, digits, punctuation and
1717// spaces. Math symbols and mathematical alphanumerics stay with the PDF font.
1818const TEXT = / ^ [ \p{ L} \p{ M} \p{ N} \p{ P} \p{ Zs} ] + $ / u;
1919const MATH_ALPHANUMERIC = / [ \u{1D400} - \u{1D7FF} ] / u;
20+ // Chinese, Japanese and Korean characters, their punctuation and full-width forms
21+ // take the CJK face; everything else takes the Latin one.
22+ const CJK =
23+ / [ \p{ Script= Han} \p{ Script= Hiragana} \p{ Script= Katakana} \p{ Script= Hangul} \p{ Script= Bopomofo} \u3000 - \u303f \u30fc \uff00 - \uffef ] / u;
2024
2125// Repainting every page per keystroke would be wasted while a name is typed.
2226const SETTLE_MS = 400 ;
@@ -39,55 +43,69 @@ export function pdfFontRole(font) {
3943export const substitutableText = ( text ) =>
4044 typeof text === 'string' && TEXT . test ( text ) && ! MATH_ALPHANUMERIC . test ( text ) ;
4145
46+ /** 'cjk' or 'text' for the face that draws a glyph's text, '' when the PDF font keeps it. */
47+ export const substituteKind = ( text ) =>
48+ substitutableText ( text ) ? ( CJK . test ( text ) ? 'cjk' : 'text' ) : '' ;
49+
4250const GENERIC = { serif : 'serif' , sans : 'sans-serif' , mono : 'monospace' } ;
4351const cssFamily = ( name , generic ) => `"${ name } ", ${ generic } ` ;
4452
45- /** pdf.js render options that draw serif, sans and mono text with local fonts, or null. */
46- export function fontSubstitutionOptions ( { serif = '' , sans = '' , mono = '' } = { } ) {
47- const families = {
48- serif : stripTranslationFont ( serif ) . trim ( ) ,
49- sans : stripTranslationFont ( sans ) . trim ( ) ,
50- mono : stripTranslationFont ( mono ) . trim ( ) ,
51- } ;
52- if ( ! families . serif && ! families . sans && ! families . mono ) return null ;
53+ const ROLES = [ 'serif' , 'sans' , 'mono' ] ;
54+ const cleanFamilies = ( names = { } ) =>
55+ Object . fromEntries ( ROLES . map ( ( role ) => [ role , stripTranslationFont ( names [ role ] || '' ) . trim ( ) ] ) ) ;
56+
57+ /**
58+ * pdf.js render options that draw serif, sans and mono text with local fonts, or
59+ * null. `cjk` names the faces for Chinese, Japanese and Korean characters.
60+ */
61+ export function fontSubstitutionOptions ( { serif = '' , sans = '' , mono = '' , cjk = { } } = { } ) {
62+ const families = { text : cleanFamilies ( { serif, sans, mono } ) , cjk : cleanFamilies ( cjk ) } ;
63+ const names = [
64+ ...ROLES . map ( ( role ) => families . text [ role ] ) ,
65+ ...ROLES . map ( ( role ) => families . cjk [ role ] ) ,
66+ ] ;
67+ if ( ! names . some ( Boolean ) ) return null ;
5368 return {
54- key : `font:${ families . serif } | ${ families . sans } | ${ families . mono } ` ,
69+ key : `font:${ names . join ( '|' ) } ` ,
5570 render : {
5671 fontSubstitution : {
5772 face ( font ) {
5873 const role = pdfFontRole ( font ) ,
59- family = families [ role ] ;
60- if ( ! family ) return null ;
74+ text = families . text [ role ] ,
75+ cjkFamily = families . cjk [ role ] ;
76+ if ( ! text && ! cjkFamily ) return null ;
6177 const name = baseName ( font . name ) ;
6278 return {
63- family : cssFamily ( family , GENERIC [ role ] ) ,
79+ text : text && cssFamily ( text , GENERIC [ role ] ) ,
80+ cjk : cjkFamily && cssFamily ( cjkFamily , GENERIC [ role ] ) ,
6481 style : font . italic || ITALIC . test ( name ) ? 'italic' : 'normal' ,
6582 weight : font . black ? '900' : font . bold || BOLD . test ( name ) ? 'bold' : 'normal' ,
6683 } ;
6784 } ,
68- accepts : substitutableText ,
85+ kind : substituteKind ,
6986 } ,
7087 } ,
7188 } ;
7289}
7390
7491const fontSet = ( preferences , prefix ) =>
75- [ 'serif' , 'sans' , 'mono' ] . map ( ( role ) => {
76- const key = `${ prefix } ${ role [ 0 ] . toUpperCase ( ) } ${ role . slice ( 1 ) } Font` ;
77- return preferences [ key ] ?. value || '' ;
78- } ) ;
92+ Object . fromEntries (
93+ ROLES . map ( ( role ) => [
94+ role ,
95+ preferences [ `${ prefix } ${ role [ 0 ] . toUpperCase ( ) } ${ role . slice ( 1 ) } Font` ] ?. value || '' ,
96+ ] ) ,
97+ ) ;
7998
8099// Local fonts for the original and the translated document, each with its own
81- // serif, sans-serif and monospace families.
100+ // serif, sans-serif and monospace families for Latin and for CJK text .
82101export function createFontSubstitution ( { preferences, onToggle } ) {
83102 const cache = { } ,
84103 timer = { id : 0 } ;
85104 const renderOptions = ( translated = false ) => {
86105 const prefix = translated ? 'translated' : 'original' ,
87- [ serif , sans , mono ] = fontSet ( preferences , prefix ) ,
88- id = `${ serif } |${ sans } |${ mono } ` ;
89- if ( cache [ prefix ] ?. id !== id )
90- cache [ prefix ] = { id, value : fontSubstitutionOptions ( { serif, sans, mono } ) } ;
106+ fonts = { ...fontSet ( preferences , prefix ) , cjk : fontSet ( preferences , `${ prefix } Cjk` ) } ,
107+ id = JSON . stringify ( fonts ) ;
108+ if ( cache [ prefix ] ?. id !== id ) cache [ prefix ] = { id, value : fontSubstitutionOptions ( fonts ) } ;
91109 return cache [ prefix ] . value ;
92110 } ;
93111 const key = ( ) => `${ renderOptions ( false ) ?. key || '' } /${ renderOptions ( true ) ?. key || '' } ` ;
0 commit comments