module Asciidoctor::Prawn::Extensions
Constants
- ColumnBox
- DetectEmptyFirstPage
- DetectEmptyFirstPageProc
- Extent
- FontAwesomeIconSets
- FontStyleToSet
- IconSetPrefixes
- IconSets
- InhibitNewPageProc
- InitialPageContent
- LineMetrics
-
:height is the height of a line
-
:leading is spacing between adjacent lines
-
:padding_top is half line spacing, plus any line_gap in the font
-
:padding_bottom is half line spacing
-
:final_gap determines whether a gap is added below the last line
-
- NewPageRequiredError
- PlaceholderChar
NOTE: must use a visible char for placeholder or else
Prawn
won’t reserve space for the fragment- Position
- ScratchExtent
Public Instance Methods
This method is a smarter version of start_new_page. It only calls start_new_page options are specified and the current page is the last page in the document. Otherwise, it advances the cursor to the next page (or column) using Bounds#move_past_bottom.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 955 def advance_page options = {} options.empty? || !last_page? ? bounds.move_past_bottom : (start_new_page options) end
Scratch
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 967 def allocate_scratch_prototype @scratch_prototype = create_scratch_prototype { ::Marshal.load ::Marshal.dump self } end
Yields to the specified block multiple times, first to determine where to render the content so it fits properly, then once more, this time providing access to the content’s extent, to ink the content in the primary document.
This method yields to the specified block in a scratch document by calling dry_run
to determine where the content should start in the primary document. In the process, it also computes the extent of the content. It then returns to the primary document and yields to the block again, this time passing in the extent of the content. The extent can be used to draw a border and/or background under the content before inking it.
This method is intended to enclose the conversion of a single content block, such as a sidebar or example block. The arrange logic attempts to keep unbreakable content on the same page, keeps the top caption pinned to the top of the content, computes the extent of the content for the purpose of drawing a border and/or background underneath it, and ensures that the extent does not begin near the bottom of a page if the first line of content doesn’t fit. If unbreakable content does not fit on a single page, the content is treated as breakable.
The block passed to this method should use advance_page
to move to the next page rather than start_new_page. Using start_new_page can mangle the calculation of content block’s extent.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 1004 def arrange_block node, &block keep_together = (node.option? 'unbreakable') && !at_page_top? doc = node.document block_for_scratch = proc do push_scratch doc instance_exec(&block) ensure pop_scratch doc end extent = dry_run keep_together: keep_together, onto: [self, keep_together], &block_for_scratch scratch? ? block_for_scratch.call : (yield extent) end
Returns whether the cursor is at the top of the page (i.e., margin box).
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 254 def at_page_top? @y == (ColumnBox === bounds ? bounds : @margin_box).absolute_top end
Returns the total left margin (to the page edge) for the current bounds.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 220 def bounds_margin_left bounds.absolute_left end
Returns the total right margin (to the page edge) for the current bounds.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 226 def bounds_margin_right page.dimensions[2] - bounds.absolute_right end
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 415 def calc_line_metrics line_height, font = self.font, font_size = self.font_size line_height_length = line_height * font_size leading = line_height_length - font_size half_leading = leading / 2 padding_top = half_leading + font.line_gap padding_bottom = half_leading LineMetrics.new line_height_length, leading, padding_top, padding_bottom, false end
Retrieves the catalog reference data for the PDF
.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 127 def catalog state.store.root end
Wraps the column_box
method and automatically sets the height unless the :height option is specified.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 697 def column_box point, options, &block options[:height] = cursor unless options.key? :height super end
Prevents at_page_top? from returning true while yielding to the specified block.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 260 def conceal_page_top old_top = (outer_bounds = ColumnBox === bounds ? bounds : @margin_box).absolute_top outer_bounds.instance_variable_set :@y, old_top + 0.0001 yield ensure outer_bounds.instance_variable_set :@y, old_top end
Deletes the current page and move the cursor to the previous page.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 865 def delete_current_page pg = page_number pdf_store = state.store content_id = page.content.identifier page_ref = page.dictionary (prune_dests = proc do |node| node.children.delete_if {|it| ::PDF::Core::NameTree::Node === it ? prune_dests[it] : it.value.data[0] == page_ref } false end)[dests.data] # NOTE: cannot delete objects and IDs, otherwise references get corrupted; so just reset the value (pdf_store.instance_variable_get :@objects)[content_id] = ::PDF::Core::Reference.new content_id, {} pdf_store.pages.data[:Kids].pop pdf_store.pages.data[:Count] -= 1 state.pages.pop if pg > 1 go_to_page pg - 1 else @page_number = 0 state.page = nil end end
Generates a destination object that resolves to the top of the page specified by the page_num parameter or the current page if no page number is provided. The destination preserves the user’s zoom level unlike the destinations generated by the outline builder.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 281 def dest_top page_num = nil dest_xyz 0, page_height, nil, (page_num ? state.pages[page_num - 1] : page) end
NOTE: override built-in draw_indented_formatted_line
to set first_line flag
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 462 def draw_indented_formatted_line string, options super string, (options.merge first_line: true) end
Yields to the specified block within the context of a scratch document up to three times to acertain the extent of the content block.
The purpose of this method is two-fold. First, it works out the position where the rendered content should start in the calling document. Then, it precomputes the extent of the content starting from that position.
This method returns the content’s extent (the range from the start page and cursor to the end page and cursor) as a ScratchExtent
object or, if the onto keyword parameter is specified, an Extent
object. A ScratchExtent
always starts the page range at 1. When the ScratchExtent
is positioned onto the primary document using ScratchExtent#position_onto, that’s when the cursor may be advanced to the next page.
This method performs all work in a scratch document (or documents). It begins by starting a new page in the scratch document, first creating the scratch document if necessary. It then applies all the settings from the main document to the scratch document that impact rendering. This includes the bounds, the cursor position, and the font settings. This method assumes that the content area remains constant when content flows from one page to the next.
From this point, the number of attempts the method makes is determined by the value of the keep_together keyword parameter. If the value is true (or the parent document is inhibiting page creation), it starts from the top of the page, yields to the block, and tries to fit the content on the current page. If the content fits, it computes and returns the ScratchExtent
(or Extent
). If the content does not fit, it first checks if this scenario should stop the operation. If the parent document is inhibiting page creation, it bubbles the error. If the single_page keyword argument is :enforce, it raises a CannotFit error. If the single_page keyword argument is true, it returns a ScratchExtent
(or Extent
) that represents a full page. If none of those conditions are met, it restarts with the keep_together parameter unset.
If the keep_together parameter is not true, the method tries to render the content in the scratch document from the location of the cursor in the main document. If the cursor is at the top of the page, no special conditions are applied (this is the last attempt). The content is rendered and the extent is computed based on where the content ended up (minus a trailing empty page). If the cursor is not at the top of the page, the method renders the content while listening for a page creation event before any content is written. If a new page is created, and no content is written on the first page, the method restarts with the cursor at the top of the page.
Note that if the block has content that itself requires a dry run, that nested dry run will be performed in a separate scratch document.
The block passed to dry run should take steps to avoid leaving the document state modified. This can be done by calling ‘push_scratch doc` at the start of the block and `pop_scratch` in the ensure clause of the block.
options - A Hash of options that configure the dry run computation:
:keep_together - A Boolean indicating whether an attempt should be made to keep the content on the same page (optional, default: false). :single_page - A Boolean indicating whether the operation should stop if the content exceeds the height of a single page. :onto - The document onto which to position the scratch extent. If this option is set, the method returns an Extent instance (optional, default: false) :pages_advanced - The number of pages the content has been advanced during this operation (internal only) (optional, default: 0)
Returns an Extent
or ScratchExtent
object that describes the bounds of the content block.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 1132 def dry_run keep_together: nil, pages_advanced: 0, single_page: nil, onto: nil, &block (scratch_pdf = scratch).start_new_page layout: page.layout, margin: page_margin saved_bounds = scratch_pdf.bounds scratch_pdf.bounds = bounds.dup.tap do |bounds_copy| bounds_copy.instance_variable_set :@document, scratch_pdf bounds_copy.instance_variable_set :@parent, saved_bounds bounds_copy.single_file if ColumnBox === bounds_copy end scratch_pdf.move_cursor_to cursor unless (scratch_start_at_top = keep_together || pages_advanced > 0 || at_page_top?) scratch_start_cursor = scratch_pdf.cursor scratch_start_page = scratch_pdf.page_number inhibit_new_page = state.on_page_create_callback == InhibitNewPageProc restart = nil scratch_pdf.font font_family, size: font_size, style: font_style do prev_font_scale, scratch_pdf.font_scale = scratch_pdf.font_scale, font_scale if keep_together || inhibit_new_page if (restart = scratch_pdf.perform_on_single_page { scratch_pdf.instance_exec(&block) }) # NOTE: propogate NewPageRequiredError from nested block, which is rendered in separate scratch document raise NewPageRequiredError if inhibit_new_page if single_page raise ::Prawn::Errors::CannotFit if single_page == :enforce # single_page and onto are mutually exclusive return ScratchExtent.new scratch_start_page, scratch_start_cursor, scratch_start_page, 0 end end elsif scratch_start_at_top scratch_pdf.instance_exec(&block) elsif (restart = scratch_pdf.stop_if_first_page_empty { scratch_pdf.instance_exec(&block) }) pages_advanced += 1 end ensure scratch_pdf.font_scale = prev_font_scale end return dry_run pages_advanced: pages_advanced, onto: onto, &block if restart scratch_end_page = scratch_pdf.page_number - scratch_start_page + (scratch_start_page = 1) if pages_advanced > 0 scratch_start_page += pages_advanced scratch_end_page += pages_advanced end scratch_end_cursor = scratch_pdf.cursor # NOTE: drop trailing blank page and move cursor to end of previous page if scratch_end_page > scratch_start_page && scratch_pdf.at_page_top? scratch_end_page -= 1 scratch_end_cursor = 0 end extent = ScratchExtent.new scratch_start_page, scratch_start_cursor, scratch_end_page, scratch_end_cursor onto ? extent.position_onto(*onto) : extent ensure scratch_pdf.bounds = saved_bounds end
Returns the effective (writable) height of the page
If inside a fixed-height bounding box, returns height of box.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 155 def effective_page_height reference_bounds.height end
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 638 def expand_grid_values shorthand, default = nil if ::Array === shorthand case shorthand.size when 1 [(value0 = shorthand[0] || default), value0] when 2 shorthand.map {|it| it || default } when 4 if Asciidoctor::PDF::ThemeLoader::CMYKColorValue === shorthand [shorthand, shorthand] else (shorthand.slice 0, 2).map {|it| it || default } end else (shorthand.slice 0, 2).map {|it| it || default } end else [(value0 = shorthand || default), value0] end end
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 610 def expand_indent_value value (::Array === value ? (value.slice 0, 2) : (::Array.new 2, value)).map(&:to_f) end
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 614 def expand_padding_value shorthand (@edge_shorthand_cache ||= ::Hash.new do |store, key| if ::Array === key case key.size when 1 value = [(value0 = key[0] || 0), value0, value0, value0] when 2 value = [(value0 = key[0] || 0), (value1 = key[1] || 0), value0, value1] when 3 value = [key[0] || 0, (value1 = key[1] || 0), key[2] || 0, value1] when 4 value = key.map {|it| it || 0 } else value = (key.slice 0, 4).map {|it| it || 0 } end else value = [(value0 = key || 0), value0, value0, value0] end store[key] = value end)[shorthand] end
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 659 def expand_rect_values shorthand, default = nil if ::Array === shorthand case shorthand.size when 1 [(value0 = shorthand[0] || default), value0, value0, value0] when 2 [(value0 = shorthand[0] || default), (value1 = shorthand[1] || default), value0, value1] when 3 [shorthand[0] || default, (value1 = shorthand[1] || default), shorthand[2] || default, value1] when 4 if Asciidoctor::PDF::ThemeLoader::CMYKColorValue === shorthand [shorthand, shorthand, shorthand, shorthand] else shorthand.map {|it| it || default } end else (shorthand.slice 0, 4).map {|it| it || default } end else [(value0 = shorthand || default), value0, value0, value0] end end
Fills the absolute bounding box with the specified fill color. Before returning from this method, the original fill color on the document is restored.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 732 def fill_absolute_bounds f_color canvas { fill_bounds f_color } end
Fills the current bounds using the specified fill color and strokes the bounds using the specified stroke color. Sets the line with if specified in the options. Before returning from this method, the original fill color, stroke color and line width on the document are restored.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 741 def fill_and_stroke_bounds f_color = fill_color, s_color = stroke_color, options = {} no_fill = !f_color || f_color == 'transparent' if ::Array === (s_width = options[:line_width] || 0) s_width = [s_width[0], s_width[1], s_width[0], s_width[1]] if s_width.size == 2 s_width_max = (s_width = s_width.map {|it| it || 0 }).max radius = 0 else radius = options[:radius] || 0 end no_stroke = !s_color || s_color == 'transparent' || (s_width_max || s_width) == 0 return if no_fill && no_stroke save_graphics_state do # fill unless no_fill fill_color f_color fill_rounded_rectangle bounds.top_left, bounds.width, bounds.height, radius end next if no_stroke # stroke if s_width_max s_width_top, s_width_right, s_width_bottom, s_width_left = s_width projection_top, projection_right, projection_bottom, projection_left = s_width.map {|it| it * 0.5 } if s_width_top > 0 stroke_horizontal_rule s_color, line_width: s_width_top, line_style: options[:line_style], left_projection: projection_left, right_projection: projection_right end if s_width_right > 0 stroke_vertical_rule s_color, line_width: s_width_right, line_style: options[:line_style], at: bounds.width, top_projection: projection_top, bottom_projection: projection_bottom end if s_width_bottom > 0 stroke_horizontal_rule s_color, line_width: s_width_bottom, line_style: options[:line_style], at: bounds.height, left_projection: projection_left, right_projection: projection_right end if s_width_left > 0 stroke_vertical_rule s_color, line_width: s_width_left, line_style: options[:line_style], top_projection: projection_top, bottom_projection: projection_bottom end else stroke_color s_color case options[:line_style] when :dashed line_width s_width dash s_width * 4 when :dotted line_width s_width dash s_width when :double single_line_width = s_width / 3.0 line_width single_line_width inner_line_offset = single_line_width * 2 inner_top_left = [bounds.left + inner_line_offset, bounds.top - inner_line_offset] stroke_rounded_rectangle bounds.top_left, bounds.width, bounds.height, radius stroke_rounded_rectangle inner_top_left, bounds.width - (inner_line_offset * 2), bounds.height - (inner_line_offset * 2), radius next else # :solid line_width s_width end stroke_rounded_rectangle bounds.top_left, bounds.width, bounds.height, radius end end end
Fills the current bounding box with the specified fill color. Before returning from this method, the original fill color on the document is restored.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 722 def fill_bounds f_color prev_fill_color = fill_color fill_color f_color fill_rectangle bounds.top_left, bounds.width, bounds.height fill_color prev_fill_color end
NOTE: override built-in fill_formatted_text_box
to insert leading before second line when :first_line is true
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 437 def fill_formatted_text_box text, options if (initial_gap = options[:initial_gap]) && !text.empty? && text[0][:from_page] != page_number self.y -= initial_gap end merge_text_box_positioning_options options box = ::Prawn::Text::Formatted::Box.new text, options remaining_fragments = box.render @no_text_printed = box.nothing_printed? @all_text_printed = box.everything_printed? unless remaining_fragments.empty? || (remaining_fragments[0][:from_page] ||= page_number) == page_number log :error, %(cannot fit formatted text on page: #{remaining_fragments.map {|it| it[:image_path] || it[:text] }.join}) page.tare_content_stream remaining_fragments = {} end if @final_gap || (options[:first_line] && !(@no_text_printed || @all_text_printed)) self.y -= box.height + box.line_gap + box.leading else self.y -= box.height end remaining_fragments end
Override the built-in float method to add support for restoring the current column of a ColumnBox
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 532 def float original_page_number = page_number original_y = y original_column = bounds.current_column if ColumnBox === bounds yield go_to_page original_page_number unless page_number == original_page_number self.y = original_y bounds.current_column = original_column if original_column end
A flowing version of bounding_box. If the content runs to another page, the cursor starts at the top of the page instead of from the original cursor position. Similar to span, except the :position option is limited to a numeric value and additional options are passed through to bounding_box.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 707 def flow_bounding_box options = {} original_y, original_x = y, bounds.absolute_left canvas do bounding_box [original_x + (options.delete :position).to_f, @margin_box.absolute_top], options do self.y = original_y yield end end end
Enhances the built-in font method to allow the font size to be specified as the second option and to lazily load font-based icons.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 321 def font name = nil, options = {} if name options = { size: options } if ::Numeric === options if IconSets.include? name ::Prawn::Icon::FontData.load self, name options = options.reject {|k| k == :style } if options.key? :style end end super end
Retrieves the current font name (i.e., family).
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 334 def font_family font.options[:family] end
Retrieves the current font info (family, style, size) as a Hash
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 342 def font_info { family: font.options[:family], style: (font.options[:style] || :normal), size: @font_size } end
Applies points as a scale factor of the current font if the value provided is less than or equal to 1 or it’s a string (e.g., 1.1em), then delegates to the super implementation to carry out the built-in functionality.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 362 def font_size points = nil return @font_size unless points if ::String === points if points.end_with? 'rem' super @root_font_size * points.to_f elsif points.end_with? 'em' super @font_size * points.to_f elsif points.end_with? '%' super @font_size * (points.to_f / 100) else super points.to_f end else super points end end
Set the font style on the document, if a style is given, otherwise return the current font style.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 348 def font_style style = nil if style font font.options[:family], style: style else font.options[:style] || :normal end end
Retreives the collection of font styles from the given font style key, which defaults to the current font style.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 397 def font_styles style = font_style FontStyleToSet[style].dup end
remove once fixed upstream; see github.com/prawnpdf/prawn/pull/1122
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 160 def generate_margin_box page_w, page_h = (page = state.page).dimensions.slice 2, 2 page_m = page.margins prev_margin_box, @margin_box = @margin_box, (::Prawn::Document::BoundingBox.new self, nil, [page_m[:left], page_h - page_m[:top]], width: page_w - page_m[:left] - page_m[:right], height: page_h - page_m[:top] - page_m[:bottom]) # update bounding box if not flowing from the previous page unless @bounding_box&.parent prev_margin_box = @bounding_box @bounding_box = @margin_box end # maintains indentation settings across page breaks if prev_margin_box @margin_box.add_left_padding prev_margin_box.total_left_padding @margin_box.add_right_padding prev_margin_box.total_right_padding end nil end
Gets the destination registered for the specified name. The return value matches that which was passed to the add_dest method.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 288 def get_dest name, node = dests.data node.children.each do |child| if ::PDF::Core::NameTree::Value === child return child.value.data if child.name == name elsif (found = get_dest name, child) return found end end nil end
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 524 def hyphenate_text text, hyphenator hyphenate_words_pcdata text, hyphenator end
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 407 def icon_font_data family ::Prawn::Icon::FontData.load self, family end
Create a new page for the specified image.
The image is positioned relative to the boundaries of the page.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 929 def image_page file, options = {} start_new_page_discretely ex = nil float do canvas do image file, ({ position: :center, vposition: :center }.merge options) rescue ex = $! end end raise ex if ex nil end
Import the specified page into the current document.
By default, advance to the next page afterwards, creating it if necessary. This behavior can be disabled by passing the option ‘advance: false`. However, due to how page creation works in Prawn
, understand that advancing to the next page is necessary to prevent the size & layout of the imported page from affecting a newly created page.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 894 def import_page file, options = {} prev_page_layout = page.layout prev_page_size = page.size prev_page_margin = page_margin prev_bounds = bounds state.compress = false if state.compress # can't use compression if using template prev_text_rendering_mode = (defined? @text_rendering_mode) ? @text_rendering_mode : nil delete_current_page if options[:replace] # NOTE: use functionality provided by prawn-templates start_new_page_discretely template: file, template_page: options[:page] # prawn-templates sets text_rendering_mode to :unknown, which breaks running content; revert @text_rendering_mode = prev_text_rendering_mode if page.imported_page? yield if block_given? # NOTE: set page size & layout explicitly in case imported page differs # I'm not sure it's right to start a new page here, but unfortunately there's no other # way atm to prevent the size & layout of the imported page from affecting subsequent pages if options.fetch :advance, true advance_page layout: prev_page_layout, margin: prev_page_margin, size: prev_page_size (@bounding_box = prev_bounds).reset_top if ColumnBox === prev_bounds end elsif options.fetch :advance_if_missing, true delete_current_page # NOTE: see previous comment advance_page layout: prev_page_layout, margin: prev_page_margin, size: prev_page_size @y = (@bounding_box = prev_bounds).reset_top if ColumnBox === prev_bounds else delete_current_page end nil end
Returns whether the current page is the last page in the document.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 270 def last_page? page_number == page_count end
Retrieves the compatiblity version of the PDF
.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 133 def min_version state.version end
Short-circuits the call to the built-in move_down
operation when n is 0.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 558 def move_down n super unless n == 0 end
Override built-in move_text_position
method to prevent Prawn
from advancing to next page if image doesn’t fit before rendering image.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 553 def move_text_position h; end
Short-circuits the call to the built-in move_up
operation when n is 0.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 545 def move_up n super unless n == 0 end
Augments the built-in pad method by adding support for specifying padding on all four sizes.
Padding may be specified as an array of four values, or as a single value. The single value is used as the padding around all four sides of the box.
If padding is nil, this method simply yields to the block and returns.
Example:
pad_box 20 do text 'A paragraph inside a blox with even padding from all edges.' end pad_box [10, 5] do text 'A paragraph inside a box with different padding from ends and sides.' end pad_box [5, 10, 15, 20] do text 'A paragraph inside a box with different padding from each edge.' end
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 585 def pad_box padding, node = nil if padding p_top, p_right, p_bottom, p_left = expand_padding_value padding # logic is intentionally inlined begin if node && ((last_block = node).content_model != :compound || (last_block = node.last_child)&.context == :paragraph) @bottom_gutters << { last_block => p_bottom } else @bottom_gutters << {} end move_down p_top bounds.add_left_padding p_left bounds.add_right_padding p_right yield ensure cursor > p_bottom ? (move_down p_bottom) : bounds.move_past_bottom unless at_page_top? @bottom_gutters.pop bounds.subtract_left_padding p_left bounds.subtract_right_padding p_right end else yield end end
Returns the height of the current page from edge-to-edge
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 147 def page_height page.dimensions[3] end
Returns the margins for the current page as a 4 element array (top, right, bottom, left)
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 190 def page_margin [page_margin_top, page_margin_right, page_margin_bottom, page_margin_left] end
Returns the width of the bottom margin for the current page
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 214 def page_margin_bottom page.margins[:bottom] end
Returns the width of the left margin for the current page
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 196 def page_margin_left page.margins[:left] end
Returns the width of the right margin for the current page
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 202 def page_margin_right page.margins[:right] end
Returns the width of the top margin for the current page
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 208 def page_margin_top page.margins[:top] end
Returns the side the current page is facing, :recto or :verso.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 232 def page_side pgnum = nil, invert = nil if invert (recto_page? pgnum) ? :verso : :recto else (verso_page? pgnum) ? :verso : :recto end end
Returns the width of the current page from edge-to-edge
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 141 def page_width page.dimensions[2] end
Parse the text into an array of fragments using the text formatter.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 425 def parse_text string, options = {} return [] if string.nil? if (format_option = options[:inline_format]) format_option = [] unless ::Array === format_option text_formatter.format string, *format_option else [text: string] end end
Perform an operation (such as creating a new page) without triggering the on_page_create callback
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 945 def perform_discretely state.on_page_create_callback = nil if (saved_callback = state.on_page_create_callback) != InhibitNewPageProc yield ensure state.on_page_create_callback = saved_callback end
This method installs an on_page_create_callback that stops processing if the first page is exceeded while yielding to the specified block. If the content fits on a single page, the processing is not stopped. The purpose of this method is to determine if the content fits on a single page.
Returns a Boolean indicating whether the content fits on a single page.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 1023 def perform_on_single_page saved_callback, state.on_page_create_callback = state.on_page_create_callback, InhibitNewPageProc yield false rescue NewPageRequiredError true ensure state.on_page_create_callback = saved_callback end
Returns whether the page is a recto page.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 242 def recto_page? pgnum = nil (pgnum || page_number).odd? end
Registers a new custom font described in the data parameter after converting the font name to a String
.
Example:
register_font Roboto: { normal: 'fonts/roboto-normal.ttf', italic: 'fonts/roboto-italic.ttf', bold: 'fonts/roboto-bold.ttf', bold_italic: 'fonts/roboto-bold_italic.ttf' }
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 313 def register_font data font_families.update data.transform_keys(&:to_s) end
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 384 def resolve_font_style styles if styles.include? :bold (styles.include? :italic) ? :bold_italic : :bold elsif styles.include? :italic :italic else :normal end end
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 411 def resolve_legacy_icon_name name ::Prawn::Icon::Compatibility::SHIMS[%(fa-#{name})] end
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 971 def scratch @scratch ||= ((Marshal.load Marshal.dump @scratch_prototype).send :init_scratch, self) end
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 975 def scratch? @label == :scratch end
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 379 def set_font font, size = nil @font = font font_size size if size end
Set the margins for the current page.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 182 def set_page_margin margin # FIXME: is there a cleaner way to set margins? does it make sense to override create_new_page? apply_margin_options margin: margin generate_margin_box end
Stretch the current bounds to the left and right edges of the current page while yielding the specified block if the verdict argument is true. Otherwise, simply yield the specified block.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 686 def span_page_width_if verdict if verdict indent(-bounds_margin_left, -bounds_margin_right) do yield end else yield end end
Start a new page without triggering the on_page_create callback
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 961 def start_new_page_discretely options = {} perform_discretely { start_new_page options } end
This method installs an on_page_create_callback that stops processing if a new page is created without writing content to the first page while yielding to the specified block. If any content is written to the first page, processing is not stopped. The purpose of this method is to check whether any content fits on the remaining space on the current page.
Returns a Boolean indicating whether any content is written on the first page.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 1039 def stop_if_first_page_empty delegate = state.on_page_create_callback state.on_page_create_callback = DetectEmptyFirstPageProc.curry[delegate].extend DetectEmptyFirstPage yield false rescue NewPageRequiredError true ensure state.on_page_create_callback = delegate end
Strokes a horizontal line using the current bounds. The width of the line can be specified using the line_width option. The offset from the cursor can be set using the at option.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 806 def stroke_horizontal_rule rule_color = stroke_color, options = {} rule_y = cursor - (options[:at] || 0) rule_style = options[:line_style] rule_width = options[:line_width] || 0.5 rule_x_start = bounds.left - (options[:left_projection] || 0) rule_x_end = bounds.right - (options[:right_projection] || 0) save_graphics_state do stroke_color rule_color case rule_style when :dashed line_width rule_width dash rule_width * 4 when :dotted line_width rule_width dash rule_width when :double single_rule_width = rule_width / 3.0 line_width single_rule_width stroke_horizontal_line rule_x_start, rule_x_end, at: (rule_y + single_rule_width) stroke_horizontal_line rule_x_start, rule_x_end, at: (rule_y - single_rule_width) next else # :solid line_width rule_width end stroke_horizontal_line rule_x_start, rule_x_end, at: rule_y end end
A compliment to the stroke_horizontal_rule
method, strokes a vertical line using the current bounds. The width of the line can be specified using the line_width option. The horizontal (x) position can be specified using the at option.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 839 def stroke_vertical_rule rule_color = stroke_color, options = {} rule_x = options[:at] || 0 rule_y_from = bounds.top + (options[:top_projection] || 0) rule_y_to = bounds.bottom - (options[:bottom_projection] || 0) rule_style = options[:line_style] rule_width = options[:line_width] || 0.5 save_graphics_state do line_width rule_width stroke_color rule_color case rule_style when :dashed dash rule_width * 4 when :dotted dash rule_width when :double stroke_vertical_line rule_y_from, rule_y_to, at: (rule_x - rule_width) rule_x += rule_width end if rule_style stroke_vertical_line rule_y_from, rule_y_to, at: rule_x end end
This method delegates to the provided block, then tares (i.e., resets) the content stream of the initial page.
The purpose of this method is to ink content while making it appear as though the page is empty. This technique allows the caller to detect whether any subsequent content was written to the page following the content inked by the block. It’s often used to keep the title of a content block with the block’s first child.
NOTE: this method should only used inside dry_run
since that’s when DetectEmptyFirstPage
is active
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 1059 def tare_first_page_content_stream return yield unless DetectEmptyFirstPage === (delegate = state.on_page_create_callback) on_page_create_called = nil state.on_page_create_callback = proc do |pdf| on_page_create_called = true pdf.state.pages[pdf.page_number - 2].tare_content_stream delegate.call pdf end begin yield ensure page.tare_content_stream unless on_page_create_called state.on_page_create_callback = delegate end end
Performs the same work as Prawn::Text.text except that the first_line_options are applied to the first line of text renderered. It’s necessary to use low-level APIs in this method so we only style the first line and not the remaining lines (which is the default behavior in Prawn
).
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 469 def text_with_formatted_first_line string, first_line_options, options if (first_line_font_color = first_line_options.delete :color) remaining_lines_font_color, options[:color] = options[:color], first_line_font_color end fragments = parse_text string, options # NOTE: the low-level APIs we're using don't recognize the :styles option, so we must resolve # NOTE: disabled until we have a need for it; currently handled in convert_abstract #if (styles = options.delete :styles) # options[:style] = resolve_font_style styles #end if (first_line_styles = first_line_options.delete :styles) first_line_options[:style] = resolve_font_style first_line_styles end first_line_text_transform = first_line_options.delete :text_transform options = options.merge document: self @final_gap = final_gap = options.delete :final_gap text_indent = options.delete :indent_paragraphs # QUESTION: should we merge more carefully here? (hand-select keys?) first_line_options = (options.merge first_line_options).merge single_line: true, first_line: true box = ::Prawn::Text::Formatted::Box.new fragments, first_line_options if text_indent remaining_fragments = indent(text_indent) { box.render dry_run: true } else remaining_fragments = box.render dry_run: true end if remaining_fragments.empty? remaining_fragments = nil elsif (remaining_fragments[0][:from_page] ||= page_number) != page_number log :error, %(cannot fit formatted text on page: #{remaining_fragments.map {|it| it[:image_path] || it[:text] }.join}) page.tare_content_stream remaining_fragments = nil end if first_line_text_transform # NOTE: applying text transform here could alter the wrapping, so isolate first line and shrink it to fit first_line_fragments = (box.instance_variable_get :@arranger).consumed fragments = first_line_fragments.map {|fragment| fragment.merge text: (transform_text fragment[:text], first_line_text_transform) } first_line_options[:overflow] = :shrink_to_fit if remaining_fragments @final_gap = true first_line_options[:force_justify] = true if first_line_options[:align] == :justify && first_line_fragments[-1][:text] != ?\n end end if text_indent indent(text_indent) { fill_formatted_text_box fragments, first_line_options } else fill_formatted_text_box fragments, first_line_options end if remaining_fragments options[:color] = remaining_lines_font_color if first_line_font_color @final_gap = final_gap if first_line_text_transform remaining_fragments = fill_formatted_text_box remaining_fragments, options draw_remaining_formatted_text_on_new_pages remaining_fragments, options end end
Returns whether the page is a verso page.
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 248 def verso_page? pgnum = nil (pgnum || page_number).even? end
Override width of string to check for placeholder char, which uses character spacing to control width
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 403 def width_of_string string, options string == PlaceholderChar ? @character_spacing : super end
# File lib/asciidoctor/pdf/ext/prawn/extensions.rb, line 979 def with_dry_run &block yield dry_run(&block).position_onto self, cursor end