Class: HexaPDF::Composer
- Inherits:
-
Object
- Object
- HexaPDF::Composer
- Defined in:
- lib/hexapdf/composer.rb
Overview
The composer class can be used to create PDF documents from scratch. It uses HexaPDF::Layout::Frame and HexaPDF::Layout::Box objects underneath and binds them together to provide a convenient interface for working with them.
Usage
First, a new Composer objects needs to be created, either using ::new or the utility method ::create.
On creation a HexaPDF::Document object is created as well the first page and an accompanying HexaPDF::Layout::Frame object. The frame is used by the various methods for general document layout tasks, like positioning of text, images, and so on. By default, it covers the whole page except the margin area. How the frame gets created can be customized by overriding the #create_frame method.
Once the Composer object is created, its methods can be used to draw text, images, … on the page. Behind the scenes HexaPDF::Layout::Box (and subclass) objects are created and drawn on the page via the frame.
If the frame of a page is full and a box doesn’t fit anymore, a new page is automatically created. The box is either split into two boxes where one fits on the first page and the other on the new page, or it is drawn completely on the new page. A new page can also be created by calling the #new_page method.
The #x and #y methods provide the point where the next box would be drawn if it fits the available space. This information can be used, for example, for custom drawing operations through #canvas which provides direct access to the HexaPDF::Content::Canvas object of the current page.
When using #canvas and modifying the graphics state, care has to be taken to avoid problems with later box drawing operations since the graphics state cannot completely be reset (e.g. transformations of the canvas cannot always be undone). So it is best to save the graphics state before and restore it afterwards.
Example
HexaPDF::Composer.create('output.pdf', margin: 36) do |pdf|
pdf.base_style.font_size(20).align(:center)
pdf.text("Hello World", valign: :center)
end
Instance Attribute Summary collapse
-
#canvas ⇒ Object
readonly
The Content::Canvas of the current page.
-
#document ⇒ Object
readonly
The PDF document that is created.
-
#frame ⇒ Object
readonly
The HexaPDF::Layout::Frame for automatic box placement.
-
#page ⇒ Object
readonly
The current page (a HexaPDF::Type::Page object).
Class Method Summary collapse
-
.create(output, **options, &block) ⇒ Object
Creates a new PDF document and writes it to
output
.
Instance Method Summary collapse
-
#box(name, width: 0, height: 0, style: nil, **box_options, &block) ⇒ Object
Draws the named box at the current position.
-
#create_stamp(width, height) {|stamp.canvas| ... } ⇒ Object
Creates a stamp (Form XObject) which can be used like an image multiple times on a single page or on multiple pages.
-
#draw_box(box) ⇒ Object
Draws the given HexaPDF::Layout::Box.
-
#formatted_text(data, width: 0, height: 0, style: nil, box_style: nil, **style_properties) ⇒ Object
Draws text like #text but allows parts of the text to be formatted differently.
-
#image(file, width: 0, height: 0, style: nil, **style_properties) ⇒ Object
Draws the given image at the current position.
-
#initialize(page_size: :A4, page_orientation: :portrait, margin: 36) {|_self| ... } ⇒ Composer
constructor
Creates a new Composer object and optionally yields it to the given block.
-
#new_page(page_size: nil, page_orientation: nil, margin: nil) ⇒ Object
Creates a new page, making it the current one.
-
#style(name, base: :base, **properties) ⇒ Object
:call-seq: composer.style(name) -> style composer.style(name, base: :base, **properties) -> style.
-
#text(str, width: 0, height: 0, style: nil, box_style: nil, **style_properties) ⇒ Object
Draws the given text at the current position into the current frame.
-
#write(output, optimize: true, **options) ⇒ Object
Writes the PDF document to the given output.
-
#x ⇒ Object
The x-position of the cursor inside the current frame.
-
#y ⇒ Object
The y-position of the cursor inside the current frame.
Constructor Details
#initialize(page_size: :A4, page_orientation: :portrait, margin: 36) {|_self| ... } ⇒ Composer
Creates a new Composer object and optionally yields it to the given block.
- page_size
-
Can be any valid predefined page size (see Type::Page::PAPER_SIZE) or an array [llx, lly, urx, ury] specifying a custom page size.
- page_orientation
-
Specifies the orientation of the page, either
:portrait
or:landscape
. Only used ifpage_size
is one of the predefined page sizes. - margin
-
The margin to use. See HexaPDF::Layout::Style::Quad#set for possible values.
Example:
composer = HexaPDF::Composer.new # uses the default values
HexaPDF::Composer.new(page_size: :Letter, margin: 72) do |composer|
#...
end
126 127 128 129 130 131 132 133 134 |
# File 'lib/hexapdf/composer.rb', line 126 def initialize(page_size: :A4, page_orientation: :portrait, margin: 36) #:yields: composer @document = HexaPDF::Document.new @page_size = page_size @page_orientation = page_orientation @margin = Layout::Style::Quad.new(margin) new_page yield(self) if block_given? end |
Instance Attribute Details
#canvas ⇒ Object (readonly)
The Content::Canvas of the current page. Can be used to perform arbitrary drawing operations.
102 103 104 |
# File 'lib/hexapdf/composer.rb', line 102 def canvas @canvas end |
#document ⇒ Object (readonly)
The PDF document that is created.
96 97 98 |
# File 'lib/hexapdf/composer.rb', line 96 def document @document end |
#frame ⇒ Object (readonly)
The HexaPDF::Layout::Frame for automatic box placement.
105 106 107 |
# File 'lib/hexapdf/composer.rb', line 105 def frame @frame end |
#page ⇒ Object (readonly)
The current page (a HexaPDF::Type::Page object).
99 100 101 |
# File 'lib/hexapdf/composer.rb', line 99 def page @page end |
Class Method Details
.create(output, **options, &block) ⇒ Object
Creates a new PDF document and writes it to output
. The options
are passed to ::new.
Example:
HexaPDF::Composer.create('output.pdf', margin: 36) do |pdf|
...
end
91 92 93 |
# File 'lib/hexapdf/composer.rb', line 91 def self.create(output, **, &block) new(**, &block).write(output) end |
Instance Method Details
#box(name, width: 0, height: 0, style: nil, **box_options, &block) ⇒ Object
Draws the named box at the current position.
It uses HexaPDF::Document::Layout#box behind the scenes to create the named box. See that method for details on the arguments.
Examples:
#>pdf-composer
composer.box(:image, image: composer.document.images.add(machu_picchu))
See: HexaPDF::Document::Layout#box
269 270 271 |
# File 'lib/hexapdf/composer.rb', line 269 def box(name, width: 0, height: 0, style: nil, **, &block) draw_box(@document.layout.box(name, width: width, height: height, style: style, **, &block)) end |
#create_stamp(width, height) {|stamp.canvas| ... } ⇒ Object
Creates a stamp (Form XObject) which can be used like an image multiple times on a single page or on multiple pages.
The width and the height of the stamp need to be set (frame.width/height or page.box.width/height might be good choices).
Examples:
#>pdf-composer
stamp = composer.create_stamp(50, 50) do |canvas|
canvas.fill_color("red").line_width(5).
rectangle(10, 10, 30, 30).fill_stroke
end
composer.image(stamp, width: 20, height: 20)
composer.image(stamp, width: 50)
322 323 324 325 326 |
# File 'lib/hexapdf/composer.rb', line 322 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 |
#draw_box(box) ⇒ Object
Draws the given HexaPDF::Layout::Box.
The box is drawn into the current frame if possible. If it doesn’t fit, the box is split. If it still doesn’t fit, a new region of the frame is determined and then the process starts again.
If none or only some parts of the box fit into the current frame, one or more new pages are created for the rest of the box.
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 |
# File 'lib/hexapdf/composer.rb', line 281 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 end |
#formatted_text(data, width: 0, height: 0, style: nil, box_style: nil, **style_properties) ⇒ Object
Draws text like #text but allows parts of the text to be formatted differently.
It uses HexaPDF::Document::Layout#formatted_text_box behind the scenes to create the HexaPDF::Layout::TextBox that does the actual work. See that method for details on the arguments.
Examples:
#>pdf-composer
composer.formatted_text(["Some string"])
composer.formatted_text(["Some ", {text: "string", fill_color: 128}])
composer.formatted_text(["Some ", {link: "https://example.com",
fill_color: 'blue', text: "Example"}])
composer.formatted_text(["Some ", {text: "string", style: {font_size: 20}}])
See: #text, HexaPDF::Layout::TextBox, HexaPDF::Layout::TextFragment
235 236 237 238 |
# File 'lib/hexapdf/composer.rb', line 235 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 |
#image(file, width: 0, height: 0, style: nil, **style_properties) ⇒ Object
Draws the given image at the current position.
It uses HexaPDF::Document::Layout#image_box behind the scenes to create the HexaPDF::Layout::ImageBox that does the actual work. See that method for details on the arguments.
Examples:
#>pdf-composer
composer.image(machu_picchu, border: {width: 3})
composer.image(machu_picchu, height: 30)
See: HexaPDF::Layout::ImageBox
253 254 255 256 |
# File 'lib/hexapdf/composer.rb', line 253 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 |
#new_page(page_size: nil, page_orientation: nil, margin: nil) ⇒ Object
Creates a new page, making it the current one.
If any of page_size
, page_orientation
or margin
are set, they will be used instead of the default values and will become the default values.
Examples:
composer.new_page # uses the default values
composer.new_page(page_size: :A5, margin: [72, 36])
145 146 147 148 149 150 151 152 153 |
# File 'lib/hexapdf/composer.rb', line 145 def new_page(page_size: nil, page_orientation: nil, margin: nil) @page_size = page_size if page_size @page_orientation = page_orientation if page_orientation @margin = Layout::Style::Quad.new(margin) if margin @page = @document.pages.add(@page_size, orientation: @page_orientation) @canvas = @page.canvas create_frame end |
#style(name, base: :base, **properties) ⇒ Object
:call-seq:
composer.style(name) -> style
composer.style(name, base: :base, **properties) -> style
Creates or updates the HexaPDF::Layout::Style object called name
with the given property values and returns it.
See HexaPDF::Document::Layout#style for details; this method is just a thin wrapper around that method.
Example:
composer.style(:base, font_size: 12, leading: 1.2)
composer.style(:header, font: 'Helvetica', fill_color: "008")
composer.style(:header1, base: :header, font_size: 30)
See: HexaPDF::Layout::Style
189 190 191 |
# File 'lib/hexapdf/composer.rb', line 189 def style(name, base: :base, **properties) @document.layout.style(name, base: base, **properties) end |
#text(str, width: 0, height: 0, style: nil, box_style: nil, **style_properties) ⇒ Object
Draws the given text at the current position into the current frame.
The text will be positioned at the current position if possible. Otherwise the next best position is used. If the text doesn’t fit onto the current page or only partially, new pages are created automatically.
This method is of the two main methods for creating text boxes, the other being #formatted_text. It uses HexaPDF::Document::Layout#text_box behind the scenes to create the HexaPDF::Layout::TextBox that does the actual work.
See HexaPDF::Document::Layout#text_box for details on the arguments.
Examples:
#>pdf-composer
composer.text("Test " * 15)
composer.text("Now " * 7, width: 100)
composer.text("Another test", font_size: 15, fill_color: "green")
composer.text("Different box style", fill_color: 'white', box_style: {
underlays: [->(c, b) { c.rectangle(0, 0, b.content_width, b.content_height).fill }]
})
214 215 216 217 |
# File 'lib/hexapdf/composer.rb', line 214 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 |
#write(output, optimize: true, **options) ⇒ Object
Writes the PDF document to the given output.
See Document#write for details.
168 169 170 |
# File 'lib/hexapdf/composer.rb', line 168 def write(output, optimize: true, **) @document.write(output, optimize: optimize, **) end |
#x ⇒ Object
The x-position of the cursor inside the current frame.
156 157 158 |
# File 'lib/hexapdf/composer.rb', line 156 def x @frame.x end |
#y ⇒ Object
The y-position of the cursor inside the current frame.
161 162 163 |
# File 'lib/hexapdf/composer.rb', line 161 def y @frame.y end |