Module: Asciidoctor::Prawn::Extensions

Includes:
Asciidoctor::PDF::Measurements, Asciidoctor::PDF::Sanitizer, Asciidoctor::PDF::TextTransformer
Included in:
Asciidoctor::PDF::Converter
Defined in:
lib/asciidoctor/pdf/ext/prawn/extensions.rb

Defined Under Namespace

Classes: Extent, LineMetrics, Position, ScratchExtent

Constant Summary collapse

ColumnBox =
::Prawn::Document::ColumnBox
FontAwesomeIconSets =
%w(fab far fas)
IconSets =
%w(fab far fas fi pf).to_set
IconSetPrefixes =
IconSets.map {|it| it + '-' }
InitialPageContent =
%(q\n)
PlaceholderChar =

NOTE: must use a visible char for placeholder or else Prawn won’t reserve space for the fragment

?\u2063
NewPageRequiredError =
::Class.new ::StopIteration
InhibitNewPageProc =
proc do |pdf|
  pdf.delete_current_page
  raise NewPageRequiredError
end
DetectEmptyFirstPage =
::Module.new
DetectEmptyFirstPageProc =
proc do |delegate, pdf|
  if pdf.state.pages[pdf.page_number - 2].empty?
    pdf.delete_current_page
    raise NewPageRequiredError
  end
  delegate.call pdf if (pdf.state.on_page_create_callback = delegate)
end

Constants included from Asciidoctor::PDF::TextTransformer

Asciidoctor::PDF::TextTransformer::BareClassRx, Asciidoctor::PDF::TextTransformer::ContiguousCharsRx, Asciidoctor::PDF::TextTransformer::Hyphen, Asciidoctor::PDF::TextTransformer::LowerAlphaChars, Asciidoctor::PDF::TextTransformer::PCDATAFilterRx, Asciidoctor::PDF::TextTransformer::SmallCapsChars, Asciidoctor::PDF::TextTransformer::SoftHyphen, Asciidoctor::PDF::TextTransformer::TagFilterRx, Asciidoctor::PDF::TextTransformer::WordRx, Asciidoctor::PDF::TextTransformer::XMLMarkupRx

Constants included from Asciidoctor::PDF::Sanitizer

Asciidoctor::PDF::Sanitizer::CharRefRx, Asciidoctor::PDF::Sanitizer::InverseXMLSpecialChars, Asciidoctor::PDF::Sanitizer::InverseXMLSpecialCharsRx, Asciidoctor::PDF::Sanitizer::SanitizeXMLRx, Asciidoctor::PDF::Sanitizer::UnescapedAmpersandRx, Asciidoctor::PDF::Sanitizer::XMLSpecialChars, Asciidoctor::PDF::Sanitizer::XMLSpecialCharsRx

Constants included from Asciidoctor::PDF::Measurements

Asciidoctor::PDF::Measurements::InsetMeasurementValueRx, Asciidoctor::PDF::Measurements::MeasurementValueHintRx, Asciidoctor::PDF::Measurements::MeasurementValueRx

Instance Method Summary collapse

Methods included from Asciidoctor::PDF::TextTransformer

#capitalize_words, #capitalize_words_pcdata, #hyphenate_words, #hyphenate_words_pcdata, #lowercase_pcdata, #smallcaps, #smallcaps_pcdata, #transform_text, #uppercase_pcdata

Methods included from Asciidoctor::PDF::Sanitizer

#encode_quotes, #escape_amp, #escape_xml, #sanitize, #unescape_xml

Methods included from Asciidoctor::PDF::Measurements

#resolve_measurement_values, #str_to_pt, #to_pt

Instance Method Details

#advance_page(options = {}) ⇒ Object

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.



955
956
957
# 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

#allocate_scratch_prototypeObject

Scratch



967
968
969
# 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

#arrange_block(node, &block) ⇒ Object

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.



1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 1004

def arrange_block node, &block
  if at_page_top?
    at_top = true
  else
    at_top = true if node.first_child? && (node.parent.attr? 'pdf-at-top')
    keep_together = true if node.option? 'unbreakable'
  end
  node.set_attr 'pdf-at-top', '' if at_top
  doc = node.document
  block_for_scratch = proc do
    at_top = node.set_attr 'pdf-at-top', '' if !at_top && at_page_top?
    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)
ensure
  node.remove_attr 'pdf-at-top' if at_top
end

#at_page_top?Boolean

Returns whether the cursor is at the top of the page (i.e., margin box).

Returns:

  • (Boolean)


254
255
256
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 254

def at_page_top?
  @y == (ColumnBox === bounds ? bounds : @margin_box).absolute_top
end

#bounds_margin_leftObject

Returns the total left margin (to the page edge) for the current bounds.



220
221
222
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 220

def bounds_margin_left
  bounds.absolute_left
end

#bounds_margin_rightObject

Returns the total right margin (to the page edge) for the current bounds.



226
227
228
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 226

def bounds_margin_right
  page.dimensions[2] - bounds.absolute_right
end

#calc_line_metrics(line_height, font = self.font, font_size = self.font_size) ⇒ Object



415
416
417
418
419
420
421
422
# 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

#catalogObject

Retrieves the catalog reference data for the PDF.



127
128
129
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 127

def catalog
  state.store.root
end

#column_box(point, options, &block) ⇒ Object

Wraps the column_box method and automatically sets the height unless the :height option is specified.



697
698
699
700
# 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

#conceal_page_topObject

Prevents at_page_top? from returning true while yielding to the specified block.



260
261
262
263
264
265
266
# 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

#delete_current_pageObject

Deletes the current page and move the cursor to the previous page.



865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
# 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

#dest_top(page_num = nil) ⇒ Object

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.



281
282
283
# 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

#draw_indented_formatted_line(string, options) ⇒ Object

NOTE: override built-in draw_indented_formatted_line to set first_line flag



462
463
464
# 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

#dry_run(keep_together: nil, pages_advanced: 0, single_page: nil, onto: nil, &block) ⇒ Object

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.



1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 1141

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

#effective_page_heightObject

Returns the effective (writable) height of the page

If inside a fixed-height bounding box, returns height of box.



155
156
157
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 155

def effective_page_height
  reference_bounds.height
end

#expand_grid_values(shorthand, default = nil) ⇒ Object



638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
# 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

#expand_indent_value(value) ⇒ Object



610
611
612
# 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

#expand_padding_value(shorthand) ⇒ Object Also known as: expand_margin_value



614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
# 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

#expand_rect_values(shorthand, default = nil) ⇒ Object



659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
# 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

#fill_absolute_bounds(f_color) ⇒ Object

Fills the absolute bounding box with the specified fill color. Before returning from this method, the original fill color on the document is restored.



732
733
734
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 732

def fill_absolute_bounds f_color
  canvas { fill_bounds f_color }
end

#fill_and_stroke_bounds(f_color = fill_color, s_color = stroke_color, options = {}) ⇒ Object

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.



741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
# 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

#fill_bounds(f_color) ⇒ Object

Fills the current bounding box with the specified fill color. Before returning from this method, the original fill color on the document is restored.



722
723
724
725
726
727
# 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

#fill_formatted_text_box(text, options) ⇒ Object

NOTE: override built-in fill_formatted_text_box to insert leading before second line when :first_line is true



437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
# 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

#floatObject

Override the built-in float method to add support for restoring the current column of a ColumnBox



532
533
534
535
536
537
538
539
540
# 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

#flow_bounding_box(options = {}) ⇒ Object

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.



707
708
709
710
711
712
713
714
715
# 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

#font(name = nil, options = {}) ⇒ Object

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.



321
322
323
324
325
326
327
328
329
330
# 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

#font_familyObject Also known as: font_name

Retrieves the current font name (i.e., family).



334
335
336
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 334

def font_family
  font.options[:family]
end

#font_infoObject

Retrieves the current font info (family, style, size) as a Hash



342
343
344
# 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

#font_size(points = nil) ⇒ Object

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.

– QUESTION: should we round the result?



362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
# 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

#font_style(style = nil) ⇒ Object

Set the font style on the document, if a style is given, otherwise return the current font style.



348
349
350
351
352
353
354
# 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

#font_styles(style = font_style) ⇒ Object

Retreives the collection of font styles from the given font style key, which defaults to the current font style.



397
398
399
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 397

def font_styles style = font_style
  FontStyleToSet[style].dup
end

#generate_margin_boxObject

remove once fixed upstream; see github.com/prawnpdf/prawn/pull/1122



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# 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

#get_dest(name, node = dests.data) ⇒ Object

Gets the destination registered for the specified name. The return value matches that which was passed to the add_dest method.



288
289
290
291
292
293
294
295
296
297
# 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

#hyphenate_text(text, hyphenator) ⇒ Object



524
525
526
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 524

def hyphenate_text text, hyphenator
  hyphenate_words_pcdata text, hyphenator
end

#icon_font_data(family) ⇒ Object



407
408
409
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 407

def icon_font_data family
  ::Prawn::Icon::FontData.load self, family
end

#image_page(file, options = {}) ⇒ Object

Create a new page for the specified image.

The image is positioned relative to the boundaries of the page.



929
930
931
932
933
934
935
936
937
938
939
940
941
# 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_page(file, options = {}) ⇒ Object

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.



894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
# 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

#last_page?Boolean

Returns whether the current page is the last page in the document.

Returns:

  • (Boolean)


270
271
272
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 270

def last_page?
  page_number == page_count
end

#min_versionObject

Retrieves the compatiblity version of the PDF.



133
134
135
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 133

def min_version
  state.version
end

#move_down(n) ⇒ Object

Short-circuits the call to the built-in move_down operation when n is 0.



558
559
560
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 558

def move_down n
  super unless n == 0
end

#move_text_position(h) ⇒ Object

Override built-in move_text_position method to prevent Prawn from advancing to next page if image doesn’t fit before rendering image. – NOTE: could use :at option when calling image/embed_image instead



553
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 553

def move_text_position h; end

#move_up(n) ⇒ Object

Short-circuits the call to the built-in move_up operation when n is 0.



545
546
547
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 545

def move_up n
  super unless n == 0
end

#pad_box(padding, node = nil) ⇒ Object

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


585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
# 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

#page_heightObject

Returns the height of the current page from edge-to-edge



147
148
149
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 147

def page_height
  page.dimensions[3]
end

#page_marginObject

Returns the margins for the current page as a 4 element array (top, right, bottom, left)



190
191
192
# 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

#page_margin_bottomObject

Returns the width of the bottom margin for the current page



214
215
216
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 214

def page_margin_bottom
  page.margins[:bottom]
end

#page_margin_leftObject

Returns the width of the left margin for the current page



196
197
198
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 196

def page_margin_left
  page.margins[:left]
end

#page_margin_rightObject

Returns the width of the right margin for the current page



202
203
204
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 202

def page_margin_right
  page.margins[:right]
end

#page_margin_topObject

Returns the width of the top margin for the current page



208
209
210
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 208

def page_margin_top
  page.margins[:top]
end

#page_side(pgnum = nil, invert = nil) ⇒ Object

Returns the side the current page is facing, :recto or :verso.



232
233
234
235
236
237
238
# 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

#page_widthObject

Returns the width of the current page from edge-to-edge



141
142
143
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 141

def page_width
  page.dimensions[2]
end

#parse_text(string, options = {}) ⇒ Object

Parse the text into an array of fragments using the text formatter.



425
426
427
428
429
430
431
432
433
434
# 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_discretelyObject

Perform an operation (such as creating a new page) without triggering the on_page_create callback



945
946
947
948
949
950
# 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

#perform_on_single_pageObject

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.



1032
1033
1034
1035
1036
1037
1038
1039
1040
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 1032

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

#recto_page?(pgnum = nil) ⇒ Boolean

Returns whether the page is a recto page.

Returns:

  • (Boolean)


242
243
244
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 242

def recto_page? pgnum = nil
  (pgnum || page_number).odd?
end

#register_font(data) ⇒ Object

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'
}


313
314
315
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 313

def register_font data
  font_families.update data.transform_keys(&:to_s)
end

#resolve_font_style(styles) ⇒ Object



384
385
386
387
388
389
390
391
392
# 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

#resolve_legacy_icon_name(name) ⇒ Object



411
412
413
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 411

def resolve_legacy_icon_name name
  ::Prawn::Icon::Compatibility::SHIMS[%(fa-#{name})]
end

#scratchObject



971
972
973
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 971

def scratch
  @scratch ||= ((Marshal.load Marshal.dump @scratch_prototype).send :init_scratch, self)
end

#scratch?Boolean

Returns:

  • (Boolean)


975
976
977
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 975

def scratch?
  @label == :scratch
end

#set_font(font, size = nil) ⇒ Object



379
380
381
382
# 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_page_margin(margin) ⇒ Object

Set the margins for the current page.



182
183
184
185
186
# 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

#span_page_width_if(verdict) ⇒ Object

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.



686
687
688
689
690
691
692
693
694
# 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_new_page_discretely(options = {}) ⇒ Object

Start a new page without triggering the on_page_create callback



961
962
963
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 961

def start_new_page_discretely options = {}
  perform_discretely { start_new_page options }
end

#stop_if_first_page_emptyObject

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.



1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 1048

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

#stroke_horizontal_rule(rule_color = stroke_color, options = {}) ⇒ Object

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.



806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
# 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

#stroke_vertical_rule(rule_color = stroke_color, options = {}) ⇒ Object

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.



839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
# 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

#tare_first_page_content_streamObject

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



1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 1068

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

#text_with_formatted_first_line(string, first_line_options, options) ⇒ Object

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).



469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
# 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

#verso_page?(pgnum = nil) ⇒ Boolean

Returns whether the page is a verso page.

Returns:

  • (Boolean)


248
249
250
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 248

def verso_page? pgnum = nil
  (pgnum || page_number).even?
end

#width_of_string(string, options) ⇒ Object

Override width of string to check for placeholder char, which uses character spacing to control width



403
404
405
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 403

def width_of_string string, options
  string == PlaceholderChar ? @character_spacing : super
end

#with_dry_run {|dry_run(&block).position_onto self, cursor| ... } ⇒ Object

Yields:

  • (dry_run(&block).position_onto self, cursor)


979
980
981
# File 'lib/asciidoctor/pdf/ext/prawn/extensions.rb', line 979

def with_dry_run &block
  yield dry_run(&block).position_onto self, cursor
end