lib/hexapdf/document/layout.rb in hexapdf-0.45.0 vs lib/hexapdf/document/layout.rb in hexapdf-0.46.0
- old
+ new
@@ -75,13 +75,13 @@
# #style). This allows one to predefine certain styles (like first level heading, second level
# heading, paragraph, ...) and consistently use them throughout the document creation process.
#
# One style property, Layout::Style#font, is handled specially:
#
- # * If no font is set on a style, the font "Times" is automatically set because otherwise there
- # would be problems with text drawing operations (font is the only style property that has no
- # valid default value).
+ # * If no font is set on a style, the default font specified via the configuration option
+ # 'font.default' is automatically set because otherwise there would be problems with text
+ # drawing operations (font is the only style property that has no valid default value).
#
# * Standard style objects only allow font wrapper objects to be set via the Layout::Style#font
# method. This class makes usage easier by allowing strings or an array [name, options_hash]
# to be used, like with e.g Content::Canvas#font. So to use Helvetica as font, one could just
# do:
@@ -149,11 +149,13 @@
end
# :nodoc:
def method_missing(name, *args, **kwargs, &block)
if @layout.box_creation_method?(name)
- @children << @layout.send(name, *args, **kwargs, &block)
+ box = @layout.send(name, *args, **kwargs, &block)
+ @children << box
+ box
else
super
end
end
@@ -352,10 +354,11 @@
box_style = (box_style ? retrieve_style(box_style) : style)
box_class_for_name(:text).new(items: text_fragments(text, style: style),
width: width, height: height, properties: properties,
style: box_style)
end
+ alias text text_box
# Creates a HexaPDF::Layout::TextBox like #text_box but allows parts of the text to be
# formatted differently.
#
# The argument +data+ needs to be an array of String, HexaPDF::Layout::InlineBox and/or Hash
@@ -454,10 +457,11 @@
end
end
box_class_for_name(:text).new(items: data, width: width, height: height,
properties: properties, style: box_style)
end
+ alias formatted_text formatted_text_box
# Creates a HexaPDF::Layout::ImageBox for the given image.
#
# The +file+ argument can be anything that is accepted by HexaPDF::Document::Images#add or a
# HexaPDF::Type::Form object.
@@ -475,10 +479,11 @@
style = retrieve_style(style, style_properties)
image = file.kind_of?(HexaPDF::Stream) ? file : @document.images.add(file)
box_class_for_name(:image).new(image: image, width: width, height: height,
properties: properties, style: style)
end
+ alias image image_box
# This helper class is used by Layout#table_box to allow specifying the keyword arguments used
# when converting cell data to box instances.
class CellArgumentCollector
@@ -493,12 +498,29 @@
@argument_infos = []
@number_of_rows = number_of_rows
@number_of_columns = number_of_columns
end
- # Stores the keyword arguments in +args+ for the given 0-based rows and columns which can
- # either be a single number or a range of numbers.
+ # Stores the hash +args+ containing styling properties for the cells specified via the given
+ # 0-based rows and columns.
+ #
+ # Rows and columns can either be single numbers, ranges of numbers or stepped ranges (i.e.
+ # Enumerator::ArithmeticSequence instances).
+ #
+ # Examples:
+ #
+ # # Gray background for all cells
+ # args[] = {cell: {background_color: "gray"}}
+ #
+ # # Cell at (2, 3) gets a bigger font size
+ # args[2, 3] = {font_size: 50}
+ #
+ # # First column of every row has bold font
+ # args[0..-1, 0] = {font: 'Helvetica bold'}
+ #
+ # # Every second row has a blue background
+ # args[(0..-1).step(2)] = {cell: {background_color: "blue"}}
def []=(rows = 0..-1, cols = 0..-1, args)
rows = adjust_range(rows.kind_of?(Integer) ? rows..rows : rows, @number_of_rows)
cols = adjust_range(cols.kind_of?(Integer) ? cols..cols : cols, @number_of_columns)
@argument_infos << ArgumentInfo.new(rows, cols, args)
end
@@ -507,11 +529,11 @@
#
# Earlier defined arguments are overridden by later ones, except for the +:cell+ key which
# is merged.
def retrieve_arguments_for(row, col)
@argument_infos.each_with_object({}) do |arg_info, result|
- next unless arg_info.rows.cover?(row) && arg_info.cols.cover?(col)
+ next unless arg_info.rows.include?(row) && arg_info.cols.include?(col)
if arg_info.args[:cell]
arg_info.args[:cell] = (result[:cell] || {}).merge(arg_info.args[:cell])
end
result.update(arg_info.args)
end
@@ -520,11 +542,12 @@
private
# Adjusts the +range+ so that both the begin and the end of the range are zero or positive
# integers smaller than +max+.
def adjust_range(range, max)
- (range.begin % max)..(range.end % max)
+ r = (range.begin % max)..(range.end % max)
+ range.kind_of?(Range) ? r : r.step(range.step)
end
end
# Creates a HexaPDF::Layout::TableBox for the given table data.
@@ -538,11 +561,12 @@
# header and footer!
#
# Additional arguments for the #text_box invocations can be specified using the optional block
# that yields a CellArgumentCollector instance. This allows customization of the text boxes.
# By specifying the special key +:cell+ it is also possible to assign style properties to the
- # cells themselves.
+ # cells themselves, irrespective of the type of content of the cells. See
+ # CellArgumentCollector#[]= for details.
#
# See HexaPDF::Layout::TableBox::new for details on +column_widths+, +header+, +footer+, and
# +cell_style+.
#
# See #text_box for details on +width+, +height+, +style+, +style_properties+ and
@@ -584,10 +608,11 @@
end
box_class_for_name(:table).new(cells: cells, column_widths: column_widths, header: header,
footer: footer, cell_style: cell_style, width: width,
height: height, properties: properties, style: style)
end
+ alias table table_box
LOREM_IPSUM = [ # :nodoc:
"Lorem ipsum dolor sit amet, con\u{00AD}sectetur adipis\u{00AD}cing elit, sed " \
"do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
"Ut enim ad minim veniam, quis nostrud exer\u{00AD}citation ullamco laboris nisi ut " \
@@ -603,26 +628,17 @@
#
# The +text_box_properties+ arguments are passed as is to #text_box.
def lorem_ipsum_box(sentences: 4, count: 1, **text_box_properties)
text_box(([LOREM_IPSUM[0, sentences].join(" ")] * count).join("\n\n"), **text_box_properties)
end
+ alias lorem_ipsum lorem_ipsum_box
- BOX_METHOD_NAMES = [:text, :formatted_text, :image, :table, :lorem_ipsum] #:nodoc:
-
- # Allows creating boxes using more convenient method names:
- #
- # * #text for #text_box
- # * #formatted_text for #formatted_text_box
- # * #image for #image_box
- # * #lorem_ipsum for #lorem_ipsum_box
- # * The name of a pre-defined box class like #column will invoke #box appropriately. Same if
- # used with a '_box' suffix.
+ # Allows creating boxes using more convenient method names: The name of a pre-defined box
+ # class like #column will invoke #box appropriately. Same if used with a '_box' suffix.
def method_missing(name, *args, **kwargs, &block)
name_without_box = name.to_s.sub(/_box$/, '').intern
- if BOX_METHOD_NAMES.include?(name)
- send("#{name}_box", *args, **kwargs, &block)
- elsif @document.config['layout.boxes.map'].key?(name_without_box)
+ if @document.config['layout.boxes.map'].key?(name_without_box)
box(name_without_box, *args, **kwargs, &block)
else
super
end
end
@@ -630,10 +646,12 @@
# :nodoc:
def respond_to_missing?(name, _private)
box_creation_method?(name) || super
end
+ BOX_METHOD_NAMES = [:text, :formatted_text, :image, :table, :lorem_ipsum] #:nodoc:
+
# :nodoc:
def box_creation_method?(name)
name = name.to_s.sub(/_box$/, '').intern
BOX_METHOD_NAMES.include?(name) || @document.config['layout.boxes.map'].key?(name) ||
name == :box
@@ -646,28 +664,31 @@
@document.config.constantize('layout.boxes.map', name) do
raise HexaPDF::Error, "Couldn't retrieve box class #{name} from configuration"
end
end
- # Retrieves the appropriate HexaPDF::Layout::Style object based on the +style+ and +properties+
- # arguments.
+ # Retrieves the appropriate HexaPDF::Layout::Style object based on the +style+ and
+ # +properties+ arguments.
#
# The +style+ argument specifies the style to retrieve. It can either be a registered style
# name (see #style), a hash with style properties or +nil+. In the latter case the registered
# style :base is used
#
# If the +properties+ hash is not empty, the retrieved style is duplicated and the properties
# hash is applied to it.
#
- # Finally, a default font (the one from the :base style or otherwise 'Times') is set if
- # necessary to ensure that the style object works in all cases.
+ # Finally, a default font (the one from the :base style or otherwise the one set using the
+ # configuration option 'font.default') is set if necessary to ensure that the style object
+ # works in all cases.
def retrieve_style(style, properties = nil)
if style.kind_of?(Symbol) && !@styles.key?(style)
raise HexaPDF::Error, "Style #{style} not defined"
end
style = HexaPDF::Layout::Style.create(@styles[style] || style || @styles[:base])
style = style.dup.update(**properties) unless properties.nil? || properties.empty?
- style.font(@styles[:base].font? && @styles[:base].font || 'Times') unless style.font?
+ unless style.font?
+ style.font(@styles[:base].font? && @styles[:base].font || @document.config['font.default'])
+ end
unless style.font.respond_to?(:pdf_object)
name, options = *style.font
style.font(@document.fonts.add(name, **(options || {})))
end
style