class HexaPDF::Composer
See: HexaPDF::Document::Layout, HexaPDF::Layout::Frame, HexaPDF::Layout::Box
end
pdf.text(“Hello World”, valign: :center)
pdf.style(:base, font_size: 20, align: :center)
HexaPDF::Composer.create(‘out.pdf’, page_size: :A6, margin: 36) do |pdf|
#>pdf-full
== Example
before and restore it afterwards.
transformations of the canvas cannot always be undone). So it is best to save the graphics state
later box drawing operations since the graphics state cannot completely be reset (e.g.
When using #canvas and modifying the graphics state, care has to be taken to avoid problems with
current page.
through #canvas which provides direct access to the HexaPDF::Content::Canvas object of the
available space. This information can be used, for example, for custom drawing operations
The #x and #y methods provide the point where the next box would be drawn if it fits the
calling the #new_page method, optionally providing a page style.
on the new page, or it is drawn completely on the new page. A new page can also be created by
created. The box is either split into two boxes where one fits on the first page and the other
If the frame of a page is full and a box doesn’t fit anymore, a new page is automatically
HexaPDF::Document::Layout methods and drawn on the page via the frame.
page. Behind the scenes HexaPDF::Layout::Box (and subclass) objects are created using the
Once the Composer object is created, its methods can be used to draw text, images, … on the
creation when creating a Composer instance.
style, see #page_style. Use the skip_page_creation
argument to avoid the initial page
except the margin area. How the frame gets created can be customized by defining a custom page
layout tasks, like positioning of text, images, and so on. By default, it covers the whole page
HexaPDF::Layout::Frame object. The frame is used by the various methods for general document
On creation a HexaPDF::Document object is created as well the first page and an accompanying
::create.
First, a new Composer objects needs to be created, either using ::new or the utility method
== Usage
provide a convenient interface for working with them.
HexaPDF::Layout::Frame and HexaPDF::Layout::Box objects underneath and binds them together to
The composer class can be used to create PDF documents from scratch. It uses
def self.create(output, **options, &block)
...
HexaPDF::Composer.create('out.pdf', margin: 36) do |pdf|
Example:
passed to ::new.
Creates a new PDF document and writes it to +output+. The argument +options+ and +block+ are
def self.create(output, **options, &block) new(**options, &block).write(output) end
def box(name, width: 0, height: 0, style: nil, **box_options, &block)
composer.box(:image, image: composer.document.images.add(machu_picchu))
#>pdf-composer
Examples:
method for details on the arguments.
It uses HexaPDF::Document::Layout#box behind the scenes to create the named box. See that
Draws the named box at the current position (see #x and #y).
def box(name, width: 0, height: 0, style: nil, **box_options, &block) draw_box(@document.layout.box(name, width: width, height: height, style: style, **box_options, &block)) end
def create_stamp(width, height) # :yield: canvas
composer.image(stamp, width: 50)
composer.image(stamp, width: 20, height: 20)
end
rectangle(10, 10, 30, 30).fill_stroke
canvas.fill_color("hp-blue").line_width(5).
stamp = composer.create_stamp(50, 50) do |canvas|
#>pdf-composer
Examples:
page.box.width/height might be good choices).
The width and the height of the stamp need to be set (frame.width/height or
or on multiple pages.
Creates a stamp (Form XObject) which can be used like an image multiple times on a single page
def create_stamp(width, height) # :yield: canvas stamp = @document.add({Type: :XObject, Subtype: :Form, BBox: [0, 0, width, height]}) yield(stamp.canvas) if block_given? stamp end
def draw_box(box)
If none or only some parts of the box fit into the current frame, one or more new pages are
again.
it still doesn't fit, a new region of the frame is determined and then the process starts
The box is drawn into the current frame if possible. If it doesn't fit, the box is split. If
Draws the given HexaPDF::Layout::Box and returns the last drawn box.
def draw_box(box) drawn_on_page = true while true result = @frame.fit(box) if result.success? @frame.draw(@canvas, result) break elsif @frame.full? new_page drawn_on_page = false else draw_box, box = @frame.split(result) if draw_box @frame.draw(@canvas, result) drawn_on_page = true elsif !@frame.find_next_region unless drawn_on_page raise HexaPDF::Error, "Box doesn't fit on empty page" end new_page drawn_on_page = false end end end box end
def formatted_text(data, width: 0, height: 0, style: nil, box_style: nil, **style_properties)
valign: :bottom, block: block}])
composer.formatted_text(["Some ", {box: :list, width: 50,
block = lambda {|list| list.text("First item"); list.text("Second item") }
composer.formatted_text(["Some ", {text: "string", style: {font_size: 20}}])
fill_color: 'hp-blue', text: "Example"}])
composer.formatted_text(["Some ", {link: "https://example.com",
composer.formatted_text(["Some ", {text: "string", fill_color: "hp-orange"}])
composer.formatted_text(["Some string"])
#>pdf-composer
Examples:
arguments.
HexaPDF::Layout::TextBox that does the actual work. See that method for details on the
It uses HexaPDF::Document::Layout#formatted_text_box behind the scenes to create the
interspersing with inline boxes.
Draws text like #text but allows parts of the text to be formatted differently and
def formatted_text(data, width: 0, height: 0, style: nil, box_style: nil, **style_properties) draw_box(@document.layout.formatted_text_box(data, width: width, height: height, style: style, box_style: box_style, **style_properties)) end
def image(file, width: 0, height: 0, style: nil, **style_properties)
composer.image(machu_picchu, height: 30)
composer.image(machu_picchu, border: {width: 3})
#>pdf-composer
Examples:
arguments.
HexaPDF::Layout::ImageBox that does the actual work. See that method for details on the
It uses HexaPDF::Document::Layout#image_box behind the scenes to create the
Draws the given image at the current position (see #x and #y).
def image(file, width: 0, height: 0, style: nil, **style_properties) draw_box(@document.layout.image_box(file, width: width, height: height, style: style, **style_properties)) end
def initialize(skip_page_creation: false, page_size: :A4, page_orientation: :portrait,
# ...
composer.new_page
end
style.frame = style.create_frame(canvas.context, 36)
composer.page_style(:default) do |canvas, style|
HexaPDF::Composer.new(skip_page_creation: true) do |composer|
end
#...
HexaPDF::Composer.new(page_size: :Letter, margin: 72) do |composer|
composer = HexaPDF::Composer.new
# Uses the default values
Example:
Only used if +skip_page_creation+ is +false+.
The margin to use. See HexaPDF::Layout::Style::Quad#set for possible values.
margin::
Only used if +skip_page_creation+ is +false+.
is one of the predefined page sizes.
Specifies the orientation of the page, either +:portrait+ or +:landscape+, if +page_size+
page_orientation::
Only used if +skip_page_creation+ is +false+.
urx, ury] specifying a custom page size.
Can be any valid predefined page size (see Type::Page::PAPER_SIZE) or an array [llx, lly,
page_size::
method to create the initial page/frame.
method needs to be used to define a page style which is then used with the #new_page
created. This is useful when the first page needs a custom page style. The #page_style
Otherwise, i.e. when this argument is +true+, no initial page or default page style is
initial page/frame is created using this page style.
and +margin+ are used to create a page style with the name :default. Additionally, an
If this argument is +false+ (the default), the arguments +page_size+, +page_orientation+
skip_page_creation::
Creates a new Composer object and optionally yields it to the given block.
def initialize(skip_page_creation: false, page_size: :A4, page_orientation: :portrait, margin: 36) #:yields: composer @document = HexaPDF::Document.new @page_styles = {} @next_page_style = :default unless skip_page_creation page_style(:default, page_size: page_size, orientation: page_orientation) do |canvas, style| style.frame = style.create_frame(canvas.context, margin) end new_page end yield(self) if block_given? end
def method_missing(name, *args, **kwargs, &block)
end
end
list.formatted_text([{text: name.to_s, fill_color: "hp-blue-dark"}, "\n#{klass}"])
composer.document.config['layout.boxes.map'].each do |name, klass|
composer.list(item_spacing: 2) do |list|
composer.lorem_ipsum(sentences: 1, margin: [0, 0, 5])
#>pdf-composer
Examples:
This includes all named boxes defined in the 'layout.boxes.map' configuration option.
Draws any custom box that can be created using HexaPDF::Document::Layout.
def method_missing(name, *args, **kwargs, &block) if @document.layout.box_creation_method?(name) draw_box(@document.layout.send(name, *args, **kwargs, &block)) else super end end
def new_page(style = @next_page_style)
composer.new_page(:cover) # uses the :cover style, set next style to :content
composer.page_style(:content, page_size: :A4)
composer.page_style(:cover, page_size: :A4, next_style: :content)
# Define two page styles
Examples:
page style, that page style is used again.
pages (see Layout::PageStyle#next_style). If this information is not provided by the applied
The applied page style determines the page style that should be used for the following new
@next_page_style).
If not provided, the currently set page style is used (:default is the initial value for
The page style (see #page_style) to use for the new page can be set via the +style+ argument.
Creates a new page, making it the current one.
def new_page(style = @next_page_style) page_style = @page_styles.fetch(style) do |key| raise ArgumentError, "Page style #{key} has not been defined" end @page = @document.pages.add(page_style.create_page(@document)) @canvas = @page.canvas @frame = page_style.frame @next_page_style = page_style.next_style || style end
def page_style(name, **attributes, &block)
end
style.frame = style.create_frame(canvas.context, 36)
end
fill
canvas.rectangle(0, 0, page_box.width, page_box.height).
canvas.fill_color("green") do
page_box = canvas.context.box
composer.page_style(:cover, page_size: :A4) do |canvas, style|
composer.page_style(:default)
Example:
is provided, it is used to define the page template.
those attribute values is created, stored under +name+ and returned. Additionally, if a block
If one or more page style attributes are given, a new HexaPDF::Layout::PageStyle object with
+nil+ is returned.
If no attributes are given, the page style +name+ is returned. In case it does not exist,
Creates and/or returns the page style +name+.
composer.page_style(name, **attributes, &template_block) -> page_style
composer.page_style(name) -> page_style
:call-seq:
def page_style(name, **attributes, &block) if attributes.empty? && block.nil? @page_styles[name] else @page_styles[name] = HexaPDF::Layout::PageStyle.new(**attributes, &block) end end
def respond_to_missing?(name, _private) # :nodoc:
def respond_to_missing?(name, _private) # :nodoc: @document.layout.box_creation_method?(name) || super end
def style(name, base: :base, **properties)
composer.style(:header1, base: :header, font_size: 30)
composer.style(:header, font: 'Helvetica', fill_color: "008")
composer.style(:base, font_size: 12, leading: 1.2)
Example:
that method.
See HexaPDF::Document::Layout#style for details; this method is just a thin wrapper around
If neither +base+ nor any style properties are specified, the style +name+ is just returned.
values and returns it.
Creates or updates the HexaPDF::Layout::Style object called +name+ with the given property
composer.style(name, base: :base, **properties) -> style
composer.style(name) -> style
:call-seq:
def style(name, base: :base, **properties) @document.layout.style(name, base: base, **properties) end
def text(str, width: 0, height: 0, style: nil, box_style: nil, **style_properties)
})
underlays: [->(c, b) { c.rectangle(0, 0, b.content_width, b.content_height).fill }]
composer.text("Different box style", fill_color: 'white', box_style: {
composer.text("Another test", font_size: 15, fill_color: "hp-blue")
composer.text("Now " * 7, width: 100)
composer.text("Test it now " * 15)
#>pdf-composer
Examples:
See HexaPDF::Document::Layout#text_box for details on the arguments.
HexaPDF::Layout::TextBox that does the actual work.
#formatted_text. It uses HexaPDF::Document::Layout#text_box behind the scenes to create the
This method is of the two main methods for creating text boxes, the other being
one or more new pages are created automatically.
next best position is used. If the text doesn't fit onto the current page or only partially,
The text will be positioned at the current position (see #x and #y) if possible. Otherwise the
Draws the given text at the current position into the current frame.
def text(str, width: 0, height: 0, style: nil, box_style: nil, **style_properties) draw_box(@document.layout.text_box(str, width: width, height: height, style: style, box_style: box_style, **style_properties)) end
def write(output, optimize: true, **options)
Writes the created PDF document to the given output.
def write(output, optimize: true, **options) @document.write(output, optimize: optimize, **options) end
def x
circle(composer.x, composer.y, 0.5).fill.
composer.canvas.stroke_color("hp-blue").
composer.text("Hello", position: :float)
#>pdf-composer
Example:
The x-position inside the current frame where the next box (provided it fits) will be placed.
def x @frame.x end
def y
circle(composer.x, composer.y, 0.5).fill.
composer.canvas.stroke_color("hp-blue").
composer.text("Hello", position: :float)
#>pdf-composer
Example:
The y-position inside the current frame.where the next box (provided it fits) will be placed.
def y @frame.y end