Class: Emfsvg::GlyphIndexMapper

Inherits:
Object
  • Object
show all
Defined in:
lib/emfsvg/glyph_index_mapper.rb

Overview

Maps glyph indices to Unicode codepoints using the system font's cmap table. Mirrors libemf2svg's fontindex_to_utf8 which uses fontconfig + freetype. We use fc-match to find the font file and fontisan to parse the cmap.

The reverse cmap is cached per (font_family, weight, italic) triple since parsing the cmap is expensive and the same font is typically referenced many times.

Constant Summary collapse

ETO_GLYPH_INDEX =

per MS-WMF ExtTextOutOptions

0x0010
HEBREW_CHARSET =

Hebrew/Arabic charsets that need RTL reversal after mapping.

177
ARABIC_CHARSET =
178

Instance Method Summary collapse

Constructor Details

#initializeGlyphIndexMapper

Returns a new instance of GlyphIndexMapper.



21
22
23
# File 'lib/emfsvg/glyph_index_mapper.rb', line 21

def initialize
  @cache = {}
end

Instance Method Details

#map(glyph_ids, font_family:, weight:, italic:, charset:) ⇒ Object

Map an array of glyph indices to a UTF-8 string using the font's reverse cmap. Mirrors libemf2svg's fontindex_to_utf8

  • text_convert:
  1. Build UTF-8 string with null chars (U+0000) for unmapped glyphs.
  2. If charset is Hebrew/Arabic, reverse the string (Ruby's String#reverse correctly handles multibyte UTF-8 chars).
  3. Truncate at the first null char — matches fprintf %s behavior.

This means: for RTL languages, if ANY glyph is unmapped, the reversed string may start with null → empty output.



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/emfsvg/glyph_index_mapper.rb', line 36

def map(glyph_ids, font_family:, weight:, italic:, charset:)
  reverse_map = build_reverse_map(font_family, weight, italic)
  return nil unless reverse_map

  codepoints = glyph_ids.map { |gid| reverse_map[gid] || 0 }
  utf8 = codepoints.pack("U*")

  if charset == HEBREW_CHARSET || charset == ARABIC_CHARSET
    utf8 = utf8.reverse
  end

  null_pos = utf8.index("\x00")
  utf8 = utf8[0, null_pos] if null_pos

  utf8
end