Class: Idml::TextEngine::LineBreaker

Inherits:
Object
  • Object
show all
Defined in:
lib/idml/text_engine/line_breaker.rb

Overview

Greedy word-wrap line breaker. Accumulates glyphs until exceeding the frame width, then breaks at the last break opportunity: a space, or a hyphen (compound words wrap after the hyphen, which stays on the first line). CJK runs get a kinsoku shori post-pass (no line starts or ends with a forbidden character).

Constant Summary collapse

HYPHEN_CODEPOINTS =

Break-after opportunities besides spaces: hyphen-minus, the Unicode hyphens, and the soft hyphen (which breaks without adding a visible hyphen).

[0x2D, 0x2010, 0x2011, 0x00AD].freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(frame_width) ⇒ LineBreaker

CJK text wraps per character (no word boundaries): an overflow whose trailing glyph is CJK breaks before it instead of emitting an overlong line.



37
38
39
# File 'lib/idml/text_engine/line_breaker.rb', line 37

def initialize(frame_width)
  @frame_width = frame_width
end

Class Method Details

.break(glyphs:, frame_width:) ⇒ Object



19
20
21
22
23
24
25
26
# File 'lib/idml/text_engine/line_breaker.rb', line 19

def self.break(glyphs:, frame_width:)
  lines = new(frame_width).break(glyphs)
  return lines unless cjk_run?(glyphs)

  lines = CjkLayout.apply_kinsoku(lines)
  CjkLayout.apply_pair_compression(lines)
  CjkLayout.apply_line_end_compression(lines)
end

Instance Method Details

#break(glyphs) ⇒ Object



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/idml/text_engine/line_breaker.rb', line 49

def break(glyphs)
  lines = []
  current = []
  current_width = 0
  break_idx = -1
  width_at_break = 0

  glyphs.each_with_index do |glyph, _idx|
    current << glyph
    current_width += glyph.width

    if break_after?(glyph)
      break_idx = current.length - 1
      width_at_break = current_width
    end

    next unless current_width > @frame_width && current.length > 1

    if break_idx >= 0
      line_glyphs = current[0..break_idx]
      lines << Line.new(line_glyphs, width_at_break, 0)
      current = current[(break_idx + 1)..]
      current_width = current.sum(&:width)
      break_idx = -1
    elsif cjk_break?(current)
      lines << Line.new(current[0..-2], current_width - glyph.width, 0)
      current = [glyph]
      current_width = glyph.width
    else
      lines << Line.new(current, current_width, 0)
      current = []
      current_width = 0
    end
  end

  lines << Line.new(current, current_width, 0) if current.any?
  lines
end