import { Glyph } from 'src/types/fontkit' ; import { toHexString , toHexStringOfMinLength } from 'src/utils' ; import { hasSurrogates , highSurrogate , isWithinBMP , lowSurrogate , } from 'src/utils/unicode' ; /** [fontId, codePoint] */ type BfChar = [ string , string ] ; /** `glyphs` should be an array of unique glyphs */ export const createCmap = ( glyphs , glyphId ) => { const bfChars = new Array ( glyphs . length ) ; for ( let idx = 0 , len = glyphs . length ; idx < len ; idx ++ ) { const glyph = glyphs [ idx ] ; const id = cmapHexFormat ( cmapHexString ( glyphId ( glyph ) ) ) ; const unicode = cmapHexFormat ( ... glyph . codePoints . map ( cmapCodePointFormat ) ) ; bfChars [ idx ] = [ id , unicode ] ; } return fillCmapTemplate ( bfChars ) ; } ; const fillCmapTemplate = ( bfChars ) => `\
/CIDInit /ProcSet findresource begin
12 dict begin
begincmap
/CIDSystemInfo <<
  /Registry (Adobe)
  /Ordering (UCS)
  /Supplement 0
>> def
/CMapName /Adobe-Identity-UCS def
/CMapType 2 def
1 begincodespacerange
<0000><ffff>
endcodespacerange
${ bfChars . length } beginbfchar
${ bfChars . map ( ( [ glyphId , codePoint ] ) => `${ glyphId } ${ codePoint }` ) . join ( '\n' ) }
endbfchar
endcmap
CMapName currentdict /CMap defineresource pop
end
end\
` ; const cmapHexFormat = ( ... values ) => `<${ values . join ( '' ) }>` ; const cmapHexString = ( value ) => toHexStringOfMinLength ( value , 4 ) ; const cmapCodePointFormat = ( codePoint ) => { if ( isWithinBMP ( codePoint ) ) return cmapHexString ( codePoint ) ; if ( hasSurrogates ( codePoint ) ) { const hs = highSurrogate ( codePoint ) ; const ls = lowSurrogate ( codePoint ) ; return `${ cmapHexString ( hs ) }${ cmapHexString ( ls ) }` ; } const hex = toHexString ( codePoint ) ; const msg = `0x${ hex } is not a valid UTF-8 or UTF-16 codepoint.` ; throw new Error ( msg ) ; } ;