class HexaPDF::Layout::TextShaper
This class is used to perform text shaping, i.e. changing the position of glyphs (e.g. for kerning) or substituting one or more glyphs for other glyphs (e.g. for ligatures).
Status of the implementation:
-
All text shaping functionality possible for Type1 fonts is implemented, i.e. kerning and ligature substitution.
-
For TrueType fonts only kerning via the 'kern' table is implemented.
Public Instance Methods
Shapes the given text fragment in-place.
The following shaping options, retrieved from the text fragment's Style#font_features
, are supported:
- :kern
-
Pair-wise kerning.
- :liga
-
Ligature substitution.
# File lib/hexapdf/layout/text_shaper.rb, line 61 def shape_text(text_fragment) font = text_fragment.style.font if text_fragment.style.font_features[:liga] && font.wrapped_font.features.include?(:liga) if font.font_type == :Type1 process_type1_ligatures(text_fragment) end text_fragment.clear_cache end if text_fragment.style.font_features[:kern] && font.wrapped_font.features.include?(:kern) case font.font_type when :TrueType process_true_type_kerning(text_fragment) when :Type1 process_type1_kerning(text_fragment) end text_fragment.clear_cache end text_fragment end
Private Instance Methods
Yields each pair of glyphs of the items array (so left must not be right + 1 if between two glyphs are one or more kerning values).
The return value of the block is taken as the next left item position.
# File lib/hexapdf/layout/text_shaper.rb, line 134 def each_glyph_pair(items) left = 0 left_item = items[left] right = 1 right_item = items[right] while left_item && right_item if left_item.kind_of?(Numeric) left += 1 left_item = items[left] right = left + 1 elsif right_item.kind_of?(Numeric) right += 1 else left = yield(left_item, right_item, left, right) left_item = items[left] right = left + 1 end right_item = items[right] end end
Processes the text fragment and does pair-wise kerning.
# File lib/hexapdf/layout/text_shaper.rb, line 113 def process_true_type_kerning(text_fragment) font = text_fragment.style.font table = font.wrapped_font[:kern].horizontal_kerning_subtable items = text_fragment.items each_glyph_pair(items) do |left_item, right_item, left, right| if (left + 1 == right) && (kerning = table.kern(left_item.id, right_item.id)) items.insert(right, -kerning * font.scaling_factor) right + 1 else right end end end
Processes the text fragment and does pair-wise kerning.
# File lib/hexapdf/layout/text_shaper.rb, line 99 def process_type1_kerning(text_fragment) pairs = text_fragment.style.font.wrapped_font.metrics.kerning_pairs items = text_fragment.items each_glyph_pair(items) do |left_item, right_item, left, right| if (left + 1 == right) && (kerning = pairs.dig(left_item.id, right_item.id)) items.insert(right, -kerning) right + 1 else right end end end
Processes the text fragment and substitutes ligatures.
# File lib/hexapdf/layout/text_shaper.rb, line 84 def process_type1_ligatures(text_fragment) items = text_fragment.items font = text_fragment.style.font pairs = font.wrapped_font.metrics.ligature_pairs each_glyph_pair(items) do |left_item, right_item, left, right| if (ligature = pairs.dig(left_item.id, right_item.id)) items[left..right] = font.glyph(ligature) left else right end end end