Text Layouter - Styling

The text used as part of a HexaPDF::Layout::TextLayouter class can be styled using HexaPDF::Layout::Style. To do this HexaPDF::Layout::TextFragment objects have to be created with the needed styling and then added to a text layout object. In addition the style objects can be used for customizing the text layouts themselves.

This example shows how to do this and shows off the various styling option, including using callbacks to further customize the appearance.

Usage:
ruby text_layouter_styling.rb [FONT_FILE]
Resulting PDF:
text_layouter_styling.pdf
Preview:

Code

require 'hexapdf'

include HexaPDF::Layout

sample_text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit
in voluptate velit esse cillum dolore eu fugiat nulla pariatur.".tr("\n", ' ')

# Wraps the text in a TextFragment using the given style.
def fragment(text, style)
  TextFragment.create(text, style)
end

# Draws the text at the given [x, y] position onto the canvas and returns the
# new y position.
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
canvas = doc.pages.add.canvas

base_font = doc.fonts.add(ARGV[0] || "Times")
base_style = {font: base_font, font_size: 12, text_indent: 20}
styles = {
  "Fonts | Font Sizes | Colors" => [
    {font: doc.fonts.add("Times", variant: :italic),
     font_size: 12, fill_color: "hp-blue-light"},
    {font: doc.fonts.add("Courier"), font_size: 14,
     fill_color: "hp-orange"},
    {font: doc.fonts.add("Helvetica", variant: :bold),
     font_size: 20, fill_alpha: 0.5},
  ],
  "Character Spacing | Word Spacing | Horizontal Scaling" => [
    {**base_style, character_spacing: 3},
    {**base_style, horizontal_scaling: 150},
    {**base_style, word_spacing: 15},
  ],
  "Text Rise" => [
    {**base_style, text_rise: 5},
    {**base_style, text_rise: -3},
  ],
  "Subscript | Superscript" => [
    {**base_style, font_size: 15, subscript: true},
    {**base_style, font_size: 15, superscript: true},
  ],
  "Underline | Strikeout" => [
    {**base_style, underline: true, strikeout: true},
    {**base_style, underline: true, strikeout: true, text_rise: 5},
    {**base_style, underline: true, strikeout: true, subscript: true},
  ],
  "Text Rendering Mode" => [
    {**base_style, text_rendering_mode: :stroke,
     stroke_width: 0.1},
    {**base_style, font_size: 20, text_rendering_mode: :fill_stroke,
     stroke_color: "hp-orange-light", stroke_width: 0.7,
     stroke_dash_pattern: [0.5, 1, 1.5], stroke_cap_style: :round},
  ],
  "Underlays | Overlays" => [
    {**base_style, underlays: [lambda do |canv, box|
       canv.fill_color("hp-orange-light2").opacity(fill_alpha: 0.5).
         rectangle(0, 0, box.width, box.height).fill
      end]},
    {**base_style, overlays: [lambda do |canv, box|
       canv.line_width(1).stroke_color("hp-teal-light2").
         line(0, -box.y_min, box.width, box.y_max - box.y_min).stroke
      end]},
  ],
  "Links" => [
    {**base_style, overlays: [
      [:link, dest: [canvas.context, :FitR, 100, 300, 200, 400]],
    ]},
    {**base_style, overlays: [
      [:link, uri: "https://hexapdf.gettalong.org",
       border: [0, 0, 2, [3, 3]], border_color: "hp-blue"],
    ]},
    {**base_style, overlays: [
      [:link, file: "text_layouter_styling.pdf", border: true],
    ]},
  ],
}

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
      fragment(str, variations[str.length % variations.length])
    elsif str.length == 2
      fragment(str, variations.first)
    elsif str =~ /Lorem/
      fragment(str, variations.last)
    else
      fragment(str, base_style)
    end
  end
  items.unshift(fragment(desc + ": ", fill_color: "hp-blue-dark", **base_style))
  y = draw_text(layouter.fit(items, width, 400), canvas, left, y) - 20
end

doc.write("text_layouter_styling.pdf", optimize: true)