Module: Idml::Render::CharacterStyle

Defined in:
lib/idml/render/character_style.rb

Overview

Applies character-level styling from a StyleResolver::StyledRun around a text-emitting block. Centralizes the IDML → PDF mapping for run-level visual properties (text color, underline rule, strike-through rule, capitalization, super/subscript position).

Used by TextFrameRenderer so character-level effects live in one place (DRY). The block typically calls canvas.text (or canvas.text_rich); this helper sets the surrounding graphics state and emits the rule lines after the block returns.

Constant Summary collapse

UNDERLINE_THICKNESS_FACTOR =
0.05
UNDERLINE_DROP_FACTOR =
0.10
STRIKE_THICKNESS_FACTOR =
0.05
STRIKE_RISE_FACTOR =
0.25

Class Method Summary collapse

Class Method Details

.apply(canvas, run, context, x:, y:, width:, size:) ⇒ Object

Yields with the run's fill color applied, then draws underline and/or strike-through rules above/below the text baseline. x, y, width, size describe the line's geometry so the rules can be positioned correctly. The block's emitted text gets the run's character-level adjustments baked in via text_kwargs (caller passes the canvas.text kwargs in, and CharacterStyle adds tracking/scale on top).



27
28
29
30
31
32
# File 'lib/idml/render/character_style.rb', line 27

def self.apply(canvas, run, context, x:, y:, width:, size:)
  apply_fill_color(canvas, run, context)
  yield
  draw_underline(canvas, run, x, y, width, size)
  draw_strike_through(canvas, run, x, y, width, size)
end

.baseline_offset(run) ⇒ Object

Returns the run's baseline shift (in PDF units). Positive values shift up; negative shift down. Used to offset the text's at: y for CSR's BaselineShift.



66
67
68
# File 'lib/idml/render/character_style.rb', line 66

def self.baseline_offset(run)
  run.baseline_shift&.nonzero? ? run.baseline_shift.to_f : 0.0
end

.position_scale(position, font_size) ⇒ Object

Returns [font_size, baseline_offset] for the CSR's Position attribute. Superscript/subscript shrink the font and shift the baseline. Normal / nil returns the size unchanged.



82
83
84
85
86
87
88
# File 'lib/idml/render/character_style.rb', line 82

def self.position_scale(position, font_size)
  case position
  when "Superscript" then [font_size * 0.583, font_size * 0.333]
  when "Subscript" then [font_size * 0.583, -font_size * 0.0833]
  else [font_size, 0.0]
  end
end

.scaling_identity?(sx, sy) ⇒ Boolean



56
57
58
# File 'lib/idml/render/character_style.rb', line 56

def self.scaling_identity?(sx, sy)
  (sx - 1.0).abs < FLOAT_EPSILON && (sy - 1.0).abs < FLOAT_EPSILON
end

.text_kwargs(run, base_kwargs) ⇒ Object

Returns the canvas.text kwargs augmented with the run's Tracking (PDF char_spacing). IDML Tracking is in points; pdfrb's char_spacing is also in text-space units, so the mapping is 1:1.



38
39
40
41
42
# File 'lib/idml/render/character_style.rb', line 38

def self.text_kwargs(run, base_kwargs)
  return base_kwargs unless run.tracking

  base_kwargs.merge(char_spacing: run.tracking.to_f)
end

.transform_text(text, capitalization) ⇒ Object

Transforms the text per the CSR's Capitalization attribute. AllCaps/SmallCaps → uppercase (true small-caps would require an OpenType feature; deferred). Other values pass through.



73
74
75
76
77
# File 'lib/idml/render/character_style.rb', line 73

def self.transform_text(text, capitalization)
  return text unless %w[AllCaps SmallCaps].include?(capitalization)

  text.upcase
end

.with_glyph_scaling(canvas, run) ⇒ Object

Yields within a transformed coordinate space when the run declares glyph scaling. IDML HorizontalScale/VerticalScale are percentages (100 = no scaling). When both are 100 / nil, yields without transformation (no graphics-state push).



48
49
50
51
52
53
54
# File 'lib/idml/render/character_style.rb', line 48

def self.with_glyph_scaling(canvas, run, &)
  sx = scale_factor(run.horizontal_scale)
  sy = scale_factor(run.vertical_scale)
  return yield if scaling_identity?(sx, sy)

  canvas.scale(sx, sy, &)
end