Skip to content

Commit

Permalink
Refactor Layout::TextLayouter class
Browse files Browse the repository at this point in the history
Move the result objects from #fit (status, lines, calculated height,
remaining items) into a separate class and return that.

Also move the #draw method to the Result class since it makes more sense
there.
  • Loading branch information
gettalong committed Sep 1, 2018
1 parent 46a3470 commit bff346a
Show file tree
Hide file tree
Showing 10 changed files with 351 additions and 425 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
## Unreleased

### Changed

* [HexaPDF::Layout::TextLayouter] to return a new
[HexaPDF::Layout::TextLayouter::Result] structure when `#fit` is called that
includes the `#draw` method

## 0.7.0 - 2018-06-19

### Changed
Expand Down
12 changes: 8 additions & 4 deletions benchmark/line_wrapping/hexapdf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@
height = 1000

doc = HexaPDF::Document.new
tl = HexaPDF::Layout::TextLayouter.create(File.read(file), width: width, height: height,
tf = HexaPDF::Layout::TextFragment.create(File.read(file),
font_features: {kern: false}, font_size: 10,
font: doc.fonts.add(ARGV[3] || "Times"))
tl.style.line_spacing(:fixed, 11.16)
tf.style.line_spacing(:fixed, 11.16)
items = [tf]
tl = HexaPDF::Layout::TextLayouter.new(tf.style)

while !tl.items.empty?
while !items.empty?
canvas = doc.pages.add([0, 0, width, height]).canvas
tl.items, = tl.draw(canvas, 0, height)
result = tl.fit(items, width: width, height: height)
result.draw(canvas, 0, height)
items = result.remaining_items
end

doc.write(ARGV[2])
6 changes: 3 additions & 3 deletions examples/text_layouter_alignment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@
width = 100
height = 150
y_base = 800
tl = HexaPDF::Layout::TextLayouter.create(sample_text, width: width,
height: height,
tf = HexaPDF::Layout::TextFragment.create(sample_text,
font: doc.fonts.add("Times"))
tl = HexaPDF::Layout::TextLayouter.new

[:left, :center, :right, :justify].each_with_index do |align, x_index|
x = x_index * (width + 20) + 70
Expand All @@ -39,7 +39,7 @@
canvas.text(valign.to_s, at: [20, y - height / 2]) if x_index == 0

tl.style.align(align).valign(valign)
tl.draw(canvas, x, y, fit: true)
tl.fit([tf], width: width, height: height).draw(canvas, x, y)
canvas.stroke_color(128, 0, 0).rectangle(x, y, width, -height).stroke
end
end
Expand Down
4 changes: 2 additions & 2 deletions examples/text_layouter_inline_boxes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@
end
end

layouter = TextLayouter.new(items: items, width: 500, height: 700)
layouter = TextLayouter.new
layouter.style.align = :justify
layouter.style.line_spacing(:proportional, 1.5)
layouter.draw(doc.pages.add.canvas, 50, 800)
layouter.fit(items, width: 500, height: 700).draw(doc.pages.add.canvas, 50, 800)

doc.write("text_layouter_inline_boxes.pdf")
11 changes: 6 additions & 5 deletions examples/text_layouter_line_wrapping.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,14 @@

x = 10
y = 220
frag = HexaPDF::Layout::TextFragment.create(text, font: doc.fonts.add("Times"))
layouter = HexaPDF::Layout::TextLayouter.new
[30, 60, 100, 160].each do |width|
layouter = HexaPDF::Layout::TextLayouter.create(text, width: width,
font: doc.fonts.add("Times"))
layouter.draw(canvas, x, y)
result = layouter.fit([frag], width: width)
result.draw(canvas, x, y)
canvas.stroke_color(255, 0, 0).line_width(0.2)
canvas.rectangle(x, y, width, -layouter.actual_height).stroke
y -= layouter.actual_height + 5
canvas.rectangle(x, y, width, -result.height).stroke
y -= result.height + 5
end

doc.write("text_layouter_line_wrapping.pdf", optimize: true)
81 changes: 35 additions & 46 deletions examples/text_layouter_shapes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#
# Both, the line widths and the horizontal offsets can be calculated given a
# certain height, and this is exactly what HexaPDF uses. If the `width` argument
# to [HexaPDF::Layout::TextLayouter::new] is an object responding to #call (e.g.
# to [HexaPDF::Layout::TextLayouter#fit] is an object responding to #call (e.g.
# a lambda), it is used for determining the line widths. And the `x_offsets`
# argument can be used in a similar way for the horizontal offsets.
#
Expand Down Expand Up @@ -41,6 +41,9 @@
ullamco laboris nisi ut aliquip ex ea commodo consequat.
".tr("\n", ' ') * 10

items = [TextFragment.create(sample_text, font: font)]
layouter = TextLayouter.new

########################################################################
# Circly things on the top
radius = 100
Expand All @@ -62,31 +65,24 @@
end

# Left: right half circle
layouter = TextLayouter.create(sample_text,
width: half_circle_widths,
height: radius * 2,
font: font)
layouter.draw(canvas, 0, circle_top)
result = layouter.fit(items, width: half_circle_widths, height: radius * 2)
result.draw(canvas, 0, circle_top)
canvas.circle(0, circle_top - radius, radius).stroke

# Center: full circle
layouter = TextLayouter.create(sample_text,
width: circle_widths,
x_offsets: left_half_circle_offsets,
height: radius * 2,
font: font,
align: :justify)
layouter.draw(canvas, page.box(:media).width / 2.0 - radius, circle_top)
layouter.style.align = :justify
result = layouter.fit(items, width: circle_widths,
x_offsets: left_half_circle_offsets,
height: radius * 2)
result.draw(canvas, page.box(:media).width / 2.0 - radius, circle_top)
canvas.circle(page.box(:media).width / 2.0, circle_top - radius, radius).stroke

# Right: left half circle
layouter = TextLayouter.create(sample_text,
width: half_circle_widths,
x_offsets: left_half_circle_offsets,
height: radius * 2,
font: font,
align: :right)
layouter.draw(canvas, page.box(:media).width - radius, circle_top)
layouter.style.align = :right
result = layouter.fit(items, width: half_circle_widths,
x_offsets: left_half_circle_offsets,
height: radius * 2)
result.draw(canvas, page.box(:media).width - radius, circle_top)
canvas.circle(page.box(:media).width, circle_top - radius, radius).stroke


Expand All @@ -111,41 +107,36 @@
end

# Left: right half diamond
layouter = TextLayouter.create(sample_text,
width: half_diamond_widths,
height: 2 * diamond_width,
font: font)
layouter.draw(canvas, 0, diamond_top)
layouter.style.align = :left
result = layouter.fit(items, width: half_diamond_widths, height: 2 * diamond_width)
result.draw(canvas, 0, diamond_top)
canvas.polyline(0, diamond_top, diamond_width, diamond_top - diamond_width,
0, diamond_top - 2 * diamond_width).stroke

# Center: full diamond
layouter = TextLayouter.create(sample_text,
width: full_diamond_widths,
x_offsets: left_half_diamond_offsets,
height: 2 * diamond_width,
font: font,
align: :justify)
layouter.style.align = :justify
result = layouter.fit(items, width: full_diamond_widths,
x_offsets: left_half_diamond_offsets,
height: 2 * diamond_width)
left = page.box(:media).width / 2.0 - diamond_width
layouter.draw(canvas, left, diamond_top)
result.draw(canvas, left, diamond_top)
canvas.polyline(left + diamond_width, diamond_top,
left + 2 * diamond_width, diamond_top - diamond_width,
left + diamond_width, diamond_top - 2 * diamond_width,
left, diamond_top - diamond_width).close_subpath.stroke

# Right: left half diamond
layouter = TextLayouter.create(sample_text,
width: half_diamond_widths,
x_offsets: left_half_diamond_offsets,
height: 2 * diamond_width,
font: font,
align: :right)
layouter.style.align = :right
result = layouter.fit(items, width: half_diamond_widths,
x_offsets: left_half_diamond_offsets,
height: 2 * diamond_width)
middle = page.box(:media).width
layouter.draw(canvas, middle - diamond_width, diamond_top)
result.draw(canvas, middle - diamond_width, diamond_top)
canvas.polyline(middle, diamond_top,
middle - diamond_width, diamond_top - diamond_width,
middle, diamond_top - 2 * diamond_width).stroke


########################################################################
# Sine wave thing at the bottom

Expand All @@ -158,13 +149,11 @@
sine_wave_widths = lambda do |height, line_height|
sine_wave_height + 100 + sine_wave_offsets.call(height, line_height) * -2
end
layouter = TextLayouter.create(sample_text,
width: sine_wave_widths,
x_offsets: sine_wave_offsets,
height: sine_wave_height,
font: font,
align: :justify)
layouter.style.align = :justify
result = layouter.fit(items, width: sine_wave_widths,
x_offsets: sine_wave_offsets,
height: sine_wave_height)
middle = page.box(:media).width / 2.0
layouter.draw(canvas, middle - (sine_wave_height + 100) / 2, sine_wave_top)
result.draw(canvas, middle - (sine_wave_height + 100) / 2, sine_wave_top)

doc.write("text_layouter_shapes.pdf", optimize: true)
14 changes: 6 additions & 8 deletions examples/text_layouter_styling.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,10 @@ def fragment(text, style)

# Draws the text at the given [x, y] position onto the canvas and returns the
# new y position.
def draw_text(layouter, canvas, x, y)
rest, = layouter.fit
raise "Error" unless rest.empty?
layouter.draw(canvas, x, y)
y - layouter.actual_height
def draw_text(result, canvas, x, y)
raise "Error" unless result.remaining_items.empty?
result.draw(canvas, x, y)
y - result.height
end

doc = HexaPDF::Document.new
Expand Down Expand Up @@ -103,7 +102,7 @@ def draw_text(layouter, canvas, x, y)
y = 800
left = 50
width = 500

layouter = TextLayouter.new(base_style)
styles.each do |desc, variations|
items = sample_text.split(/(Lorem ipsum dolor|\b\w{2,5}\b)/).map do |str|
if str.length >= 3 && str.length <= 5
Expand All @@ -117,8 +116,7 @@ def draw_text(layouter, canvas, x, y)
end
end
items.unshift(fragment(desc + ": ", fill_color: [255, 0, 0], **base_style))
layouter = TextLayouter.new(items: items, width: width, style: base_style)
y = draw_text(layouter, canvas, left, y) - 20
y = draw_text(layouter.fit(items, width: width), canvas, left, y) - 20
end

doc.write("text_layouter_styling.pdf", optimize: true)
4 changes: 2 additions & 2 deletions lib/hexapdf/layout/line.rb
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,8 @@ def simulate_height(item)
# An optional vertical offset that should be taken into account when positioning the line.
#
# For the first line in a paragraph this describes the offset from the top of the box to the
# top of the line. For all other lines it describes the offset from the previous baseline to
# the baseline of this line.
# baseline of the line. For all other lines it describes the offset from the previous baseline
# to the baseline of this line.
attr_accessor :y_offset

# Creates a new Line object, adding all given items to it.
Expand Down
Loading

0 comments on commit bff346a

Please sign in to comment.