Class: HexaPDF::Layout::TextFragment
- Inherits:
-
Object
- Object
- HexaPDF::Layout::TextFragment
- Defined in:
- lib/hexapdf/layout/text_fragment.rb
Overview
A TextFragment describes an optionally kerned piece of text that shares the same font, font size and other properties.
Its items are either glyph objects of the font or numeric values describing kerning information. All returned measurement values are in text space units. If the items or the style are changed, the #clear_cache has to be called. Otherwise the measurements may not be correct!
The items of a text fragment may be frozen to indicate that the fragment is potentially used multiple times.
The rectangle with the bottom left corner (#x_min, #y_min) and the top right corner (#x_max, #y_max) describes the minimum bounding box of the whole text fragment and is usually not equal to the box (0, 0)-(#width, #height).
Constant Summary collapse
- PRECISION =
The precision used to determine whether two floats represent the same value.
0.000001
Instance Attribute Summary collapse
-
#items ⇒ Object
The items (glyphs and kerning values) of the text fragment.
-
#style ⇒ Object
readonly
The style to be applied.
Class Method Summary collapse
-
.create(text, style = nil, **options) ⇒ Object
Creates a new TextFragment object for the given text, shapes it and returns it.
Instance Method Summary collapse
-
#clear_cache ⇒ Object
Clears all cached values.
-
#draw(canvas, x, y, ignore_text_properties: false) ⇒ Object
Draws the text onto the canvas at the given position.
-
#exact_y_max ⇒ Object
The maximum y-coordinate of any item.
-
#exact_y_min ⇒ Object
The minimum y-coordinate of any item.
-
#height ⇒ Object
The height of the text fragment.
-
#initialize(items, style) ⇒ TextFragment
constructor
Creates a new TextFragment object with the given items and style.
-
#inspect ⇒ Object
:nodoc:.
-
#valign ⇒ Object
Returns the vertical alignment inside a line which is always :text for text fragments.
-
#width ⇒ Object
The width of the text fragment.
-
#x_max ⇒ Object
The maximum x-coordinate of the last glyph.
-
#x_min ⇒ Object
The minimum x-coordinate of the first glyph.
-
#y_max ⇒ Object
The maximum y-coordinate, calculated using the scaled ascender of the font.
-
#y_min ⇒ Object
The minimum y-coordinate, calculated using the scaled descender of the font.
Constructor Details
#initialize(items, style) ⇒ TextFragment
Creates a new TextFragment object with the given items and style.
The argument style
can either be a Style object or a hash of style options.
104 105 106 107 |
# File 'lib/hexapdf/layout/text_fragment.rb', line 104 def initialize(items, style) @items = items @style = (style.kind_of?(Style) ? style : Style.new(style)) end |
Instance Attribute Details
#items ⇒ Object
The items (glyphs and kerning values) of the text fragment.
71 72 73 |
# File 'lib/hexapdf/layout/text_fragment.rb', line 71 def items @items end |
#style ⇒ Object (readonly)
The style to be applied.
Only the following properties are used:
-
Style#font
-
Style#font_size
-
Style#horizontal_scaling
-
Style#character_spacing
-
Style#word_spacing
-
Style#text_rise
-
Style#text_rendering_mode
-
Style#subscript
-
Style#superscript
-
Style#underline
-
Style#strikeout
-
Style#fill_color
-
Style#fill_alpha
-
Style#stroke_color
-
Style#stroke_alpha
-
Style#stroke_width
-
Style#stroke_cap_style
-
Style#stroke_join_style
-
Style#stroke_miter_limit
-
Style#stroke_dash_pattern
-
Style#underlay_callback
-
Style#overlay_callback
99 100 101 |
# File 'lib/hexapdf/layout/text_fragment.rb', line 99 def style @style end |
Class Method Details
.create(text, style = nil, **options) ⇒ Object
Creates a new TextFragment object for the given text, shapes it and returns it.
The needed style of the text fragment can either be specified by the style
argument or via the options
(in which case a new Style object is created). Regardless of the way, the resulting style object needs at least the font set.
64 65 66 67 68 |
# File 'lib/hexapdf/layout/text_fragment.rb', line 64 def self.create(text, style = nil, **) style = (style.nil? ? Style.new() : style) fragment = new(style.font.decode_utf8(text), style) TextShaper.new.shape_text(fragment) end |
Instance Method Details
#clear_cache ⇒ Object
Clears all cached values.
This method needs to be called if the fragment’s items or attributes are changed!
265 266 267 268 |
# File 'lib/hexapdf/layout/text_fragment.rb', line 265 def clear_cache @x_min = @x_max = @exact_y_min = @exact_y_max = @width = @height = nil self end |
#draw(canvas, x, y, ignore_text_properties: false) ⇒ Object
Draws the text onto the canvas at the given position.
This method is the main styled text drawing facility and therefore some optimizations are done:
-
The text is drawn using HexaPDF::Content;:Canvas#show_glyphs_only which means that the text matrix is not updated. Therefore the caller must not rely on it!
-
All text style properties mentioned in the description of #style are set except if
ignore_text_properties
is set totrue
. Note that this only applies to style properties that directly affect text drawing, so, for example, underlays/overlays and underlining/strikeout is always done.The caller should set
ignore_text_properties
totrue
if the graphics state hasn’t been changed. This is the case, for example, if the last thing drawn was a text fragment with the same style. -
It is assumed that the text matrix is not rotated, skewed, etc. so that setting the text position can be done using the optimal method.
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/hexapdf/layout/text_fragment.rb', line 131 def draw(canvas, x, y, ignore_text_properties: false) style.underlays.draw(canvas, x, y + y_min, self) if style.underlays? # Set general font related graphics state if necessary unless ignore_text_properties canvas.font(style.font, size: style.calculated_font_size). horizontal_scaling(style.horizontal_scaling). character_spacing(style.character_spacing). word_spacing(style.word_spacing). text_rise(style.calculated_text_rise). text_rendering_mode(style.text_rendering_mode) # Set fill and/or stroke related graphics state canvas.opacity(fill_alpha: style.fill_alpha, stroke_alpha: style.stroke_alpha) trm = canvas.text_rendering_mode if trm.value.even? # text is filled canvas.fill_color(style.fill_color) end if trm == :stroke || trm == :fill_stroke || trm == :stroke_clip || trm == :fill_stroke_clip canvas.stroke_color(style.stroke_color). line_width(style.stroke_width). line_cap_style(style.stroke_cap_style). line_join_style(style.stroke_join_style). miter_limit(style.stroke_miter_limit). line_dash_pattern(style.stroke_dash_pattern) end end canvas.begin_text tlm = canvas.graphics_state.tlm tx = x - tlm.e ty = y - tlm.f if tx.abs < PRECISION if (ty + canvas.graphics_state.leading).abs < PRECISION canvas.move_text_cursor else canvas.move_text_cursor(offset: [0, ty], absolute: false) end elsif ty.abs < PRECISION canvas.move_text_cursor(offset: [tx, 0], absolute: false) else canvas.move_text_cursor(offset: [x, y]) end canvas.show_glyphs_only(items) if style.underline? && style.underline y_offset = style.calculated_underline_position canvas.save_graphics_state do canvas.stroke_color(style.fill_color). line_width(style.calculated_underline_thickness). line_cap_style(:butt). line_dash_pattern(0). line(x, y + y_offset, x + width, y + y_offset). stroke end end if style.strikeout? && style.strikeout y_offset = style.calculated_strikeout_position canvas.save_graphics_state do canvas.stroke_color(style.fill_color). line_width(style.calculated_strikeout_thickness). line_cap_style(:butt). line_dash_pattern(0). line(x, y + y_offset, x + width, y + y_offset). stroke end end style..draw(canvas, x, y + y_min, self) if style. end |
#exact_y_max ⇒ Object
The maximum y-coordinate of any item.
230 231 232 233 |
# File 'lib/hexapdf/layout/text_fragment.rb', line 230 def exact_y_max @exact_y_max ||= (@items.max_by(&:y_max)&.y_max || 0) * style.calculated_font_size / 1000.0 + style.calculated_text_rise end |
#exact_y_min ⇒ Object
The minimum y-coordinate of any item.
224 225 226 227 |
# File 'lib/hexapdf/layout/text_fragment.rb', line 224 def exact_y_min @exact_y_min ||= (@items.min_by(&:y_min)&.y_min || 0) * style.calculated_font_size / 1000.0 + style.calculated_text_rise end |
#height ⇒ Object
The height of the text fragment.
It is calculated as the difference of the maximum of the y_max
values and the minimum of the y_min
values of the items. However, the text rise value is also taken into account so that the baseline is always inside the bounds. For example, if a large negative text rise value is used, the baseline will be equal to the top boundary; if a large positive value is used, it will be equal to the bottom boundary.
251 252 253 |
# File 'lib/hexapdf/layout/text_fragment.rb', line 251 def height @height ||= [y_max, 0].max - [y_min, 0].min end |
#inspect ⇒ Object
:nodoc:
271 272 273 |
# File 'lib/hexapdf/layout/text_fragment.rb', line 271 def inspect "#<#{self.class.name} #{items.inspect}>" end |
#valign ⇒ Object
Returns the vertical alignment inside a line which is always :text for text fragments.
See Line for details.
258 259 260 |
# File 'lib/hexapdf/layout/text_fragment.rb', line 258 def valign :text end |
#width ⇒ Object
The width of the text fragment.
It is the sum of the widths of its items and is calculated by using the algorithm presented in PDF1.7 s9.4.4. By using kerning values as the first and/or last items, the text contained in the fragment may spill over the left and/or right boundary.
240 241 242 |
# File 'lib/hexapdf/layout/text_fragment.rb', line 240 def width @width ||= @items.sum {|item| style.scaled_item_width(item) } end |
#x_max ⇒ Object
The maximum x-coordinate of the last glyph.
209 210 211 |
# File 'lib/hexapdf/layout/text_fragment.rb', line 209 def x_max @x_max ||= calculate_x_max end |
#x_min ⇒ Object
The minimum x-coordinate of the first glyph.
204 205 206 |
# File 'lib/hexapdf/layout/text_fragment.rb', line 204 def x_min @x_min ||= calculate_x_min end |
#y_max ⇒ Object
The maximum y-coordinate, calculated using the scaled ascender of the font.
219 220 221 |
# File 'lib/hexapdf/layout/text_fragment.rb', line 219 def y_max style.scaled_y_max end |
#y_min ⇒ Object
The minimum y-coordinate, calculated using the scaled descender of the font.
214 215 216 |
# File 'lib/hexapdf/layout/text_fragment.rb', line 214 def y_min style.scaled_y_min end |