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 Frame and Box objects underneath.
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.
The base style that is used by all these boxes can be defined using the #base_style method which returns a HexaPDF::Layout::Style object. The only style property that is set by default is the font (Times) because otherwise there would be problems with text drawing operations (font is the only style property that has no valid default value).
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
-
#base_style ⇒ Object
readonly
The base style which is used when no explicit style is provided to methods (e.g. to #text).
-
#canvas ⇒ Object
readonly
The Content::Canvas of the current page.
-
#document ⇒ Object
readonly
The PDF document that is created.
-
#frame ⇒ Object
readonly
The 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
-
#draw_box(box) ⇒ Object
Draws the given Layout::Box.
-
#formatted_text(data, width: 0, height: 0, style: nil, **options) ⇒ Object
Draws text like #text but where parts of it can be formatted differently.
-
#image(file, width: 0, height: 0, style: nil, **options) ⇒ Object
Draws the given image file 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.
-
#text(str, width: 0, height: 0, style: nil, **options) ⇒ 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
:portraitor:landscape. Only used ifpage_sizeis one of the predefined page sizes. - margin
-
The margin to use. See Layout::Style::Quad#set for possible values.
126 127 128 129 130 131 132 133 134 135 |
# 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 @base_style = Layout::Style.new(font: 'Times') yield(self) if block_given? end |
Instance Attribute Details
#base_style ⇒ Object (readonly)
The base style which is used when no explicit style is provided to methods (e.g. to #text).
112 113 114 |
# File 'lib/hexapdf/composer.rb', line 112 def base_style @base_style end |
#canvas ⇒ Object (readonly)
The Content::Canvas of the current page. Can be used to perform arbitrary drawing operations.
106 107 108 |
# File 'lib/hexapdf/composer.rb', line 106 def canvas @canvas end |
#document ⇒ Object (readonly)
The PDF document that is created.
100 101 102 |
# File 'lib/hexapdf/composer.rb', line 100 def document @document end |
#frame ⇒ Object (readonly)
The Layout::Frame for automatic box placement.
109 110 111 |
# File 'lib/hexapdf/composer.rb', line 109 def frame @frame end |
#page ⇒ Object (readonly)
The current page (a HexaPDF::Type::Page object).
103 104 105 |
# File 'lib/hexapdf/composer.rb', line 103 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
95 96 97 |
# File 'lib/hexapdf/composer.rb', line 95 def self.create(output, **, &block) new(**, &block).write(output) end |
Instance Method Details
#draw_box(box) ⇒ Object
Draws the given 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.
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 |
# File 'lib/hexapdf/composer.rb', line 258 def draw_box(box) drawn_on_page = true while true if @frame.fit(box) @frame.draw(@canvas, box) break elsif @frame.full? new_page drawn_on_page = false else draw_box, box = @frame.split(box) if draw_box @frame.draw(@canvas, draw_box) 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, **options) ⇒ Object
Draws text like #text but where parts of it can be formatted differently.
The argument data needs to be an array of String or Hash objects:
-
A String object is treated like data.
-
Hashes can contain any style properties and the following special keys:
- text
-
The text to be formatted.
- link
-
A URL that should be linked to. If no text is provided but a link, the link is used as text.
- style
-
A Layout::Style object to use as basis instead of the style created from the
styleandoptionsarguments.
If any style properties are set, the used style is copied and the additional properties applied.
Examples:
composer.formatted_text(["Some string"]) # The same as #text
composer.formatted_text(["Some ", {text: "string", fill_color: 128}]
composer.formatted_text(["Some ", {link: "https://example.com", text: "Example"}])
composer.formatted_text(["Some ", {text: "string", style: my_style}])
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 |
# File 'lib/hexapdf/composer.rb', line 221 def formatted_text(data, width: 0, height: 0, style: nil, **) style = update_style(style, ) data.map! do |hash| if hash.kind_of?(String) Layout::TextFragment.create(hash, style) else link = hash.delete(:link) text = hash.delete(:text) || link || "" used_style = update_style(hash.delete(:style), ) || style if link || !hash.empty? used_style = used_style.dup hash.each {|key, value| used_style.send(key, value) } used_style..add(:link, uri: link) if link end Layout::TextFragment.create(text, used_style) end end draw_box(Layout::TextBox.new(data, width: width, height: height, style: style)) end |
#image(file, width: 0, height: 0, style: nil, **options) ⇒ Object
Draws the given image file at the current position.
See #text for details on width, height, style and options.
244 245 246 247 248 |
# File 'lib/hexapdf/composer.rb', line 244 def image(file, width: 0, height: 0, style: nil, **) style = update_style(style, ) image = document.images.add(file) draw_box(Layout::ImageBox.new(image, width: width, height: height, style: style)) 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])
146 147 148 149 150 151 152 153 154 |
# File 'lib/hexapdf/composer.rb', line 146 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 |
#text(str, width: 0, height: 0, style: nil, **options) ⇒ Object
Draws the given text at the current position into the current frame.
This method is the main method for displaying text on a PDF page. It uses a Layout::TextBox behind the scenes to do the actual work.
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.
The arguments width and height are used as constraints and are respected when fitting the box.
The text is styled using the given style object (see Layout::Style) or, if no style object is specified, the base style (see #base_style). If any additional style options are specified, the used style is copied and the additional styles are applied.
See HexaPDF::Layout::TextBox for details.
190 191 192 193 194 |
# File 'lib/hexapdf/composer.rb', line 190 def text(str, width: 0, height: 0, style: nil, **) style = update_style(style, ) draw_box(Layout::TextBox.new([Layout::TextFragment.create(str, style)], width: width, height: height, style: style)) end |
#write(output, optimize: true, **options) ⇒ Object
Writes the PDF document to the given output.
See Document#write for details.
169 170 171 |
# File 'lib/hexapdf/composer.rb', line 169 def write(output, optimize: true, **) @document.write(output, optimize: optimize, **) end |
#x ⇒ Object
The x-position of the cursor inside the current frame.
157 158 159 |
# File 'lib/hexapdf/composer.rb', line 157 def x @frame.x end |
#y ⇒ Object
The y-position of the cursor inside the current frame.
162 163 164 |
# File 'lib/hexapdf/composer.rb', line 162 def y @frame.y end |