The system font path. The sytem font path will be determined differently for each operating system.
Uses ENV/Fonts as the system font path. There is an extension that will handle this better, but until and unless it is distributed with the standard Ruby Windows installer, PDF::Writer will not depend upon it.
The fonts are found in /System/Library/Fonts.
The font path list will be found (usually) in /etc/fonts/fonts.conf or /usr/etc/fonts/fonts.conf. This XML file will be parsed (using REXML) to provide the value for FONT_PATH.
Standard page size names. One of these may be provided to ::new as the :paper
parameter.
Page sizes supported are:
4A0, 2A0
A0, A1 A2, A3, A4, A5, A6, A7, A8, A9, A10
B0, B1, B2, B3, B4, B5, B6, B7, B8, B9, B10
C0, C1, C2, C3, C4, C5, C6, C7, C8, C9, C10
RA0, RA1, RA2, RA3, RA4
SRA0, SRA1, SRA2, SRA3, SRA4
LETTER
LEGAL
FOLIO
EXECUTIVE
Callback tag relationships. All relationships are of the form “tagname” => CallbackClass.
There are three types of tag callbacks:
:pair
Paired callbacks, e.g., <c:alink></c:alink>.
:single
Single-tag callbacks, e.g., <C:bullet>.
:replace
Single-tag replacement callbacks, e.g., <r:xref>.
The version of PDF::Writer.
Returns the absolute y position of the bottom margin.
The absolute x position of the left margin.
The absolute x position of the right margin.
Returns the absolute y position of the top margin.
The absolute x middle position.
The absolute y middle position.
The total number of columns. Returns zero (0) if columns are off.
The gutter between columns. This will return zero (0) if columns are off.
The current column number. Returns zero (0) if columns are off.
The width of the currently active column. This will return zero (0) if columns are off.
Sets the document to compressed (true
) or uncompressed
(false
). Defaults to uncompressed. This can ONLY be set once
and should be set as early as possible in the document creation process.
Returns the current contents object to which raw PDF instructions may be written.
The string that will be used to encrypt this PDF document.
Allows the user to find out what the ID is of the first page that was created during startup - useful if they wish to add something to it later.
Add a new translation table for a font family. A font family will be used to associate a single name and font styles with multiple fonts. A style will be identified with a single-character style identifier or a series of style identifiers. The only styles currently recognised are:
b
Bold (or heavy) fonts. Examples: Helvetica-Bold, Courier-Bold, Times-Bold.
i
Italic (or oblique) fonts. Examples: Helvetica-Oblique, Courier-Oblique, Times-Italic.
bi
Bold italic fonts. Examples Helvetica-BoldOblique, Courier-BoldOblique, Times-BoldItalic.
ib
Italic bold fonts. Generally defined the same as bi
font
styles. Examples: Helvetica-BoldOblique, Courier-BoldOblique,
Times-BoldItalic.
Each font family key is the base name for the font.
The PDF::Writer::Object::Info info object. This is used to provide certain metadata.
The height of the margin area.
The width of the margin area.
The middle of the writing area between the left and right margins.
The middle of the writing area between the top and bottom margins.
The vertical position of the writing point. If the vertical position is outside of the bottom margin, a new page will be created.
The version of PDF to which this document conforms. Should be one of PDF_VERSION_13, PDF_VERSION_14, PDF_VERSION_15, or PDF_VERSION_16.
The vertical position of the writing point. The vertical position is constrained between the top and bottom margins. Any attempt to set it outside of those margins will cause the y pointer to be placed absolutely at the margins.
memory improvement for transaction-simple
# File lib/pdf/writer.rb, line 2725 def _post_transaction_rewind @objects.each { |e| e.instance_variable_set(:@parent,self) } end
add content to the currently active object
# File lib/pdf/writer.rb, line 1029 def add_content(cc) @current_contents << cc end
Create a labelled destination within the document. The label is the name which will be used for <c:ilink> destinations.
The viewport will be opened at position (left, top)
with
zoom
percentage. params
must have three values
representing left
, top
, and zoom
,
respectively. If the values are “null”, the current parameter values
are unchanged.
Fit the page to the viewport (horizontal and vertical). params
will be ignored.
Fit the page horizontally to the viewport. The top of the viewport is set
to the first value in params
.
Fit the page vertically to the viewport. The left of the viewport is set to
the first value in params
.
Fits the page to the provided rectangle. params
must have four
values representing the left
, bottom
,
right
, and top
positions, respectively.
Fits the page to the bounding box of the page. params
is
ignored.
Fits the page horizontally to the bounding box of the page. The top
position is defined by the first value in params
.
Fits the page vertically to the bounding box of the page. The left position
is defined by the first value in params
.
# File lib/pdf/writer.rb, line 1856 def add_destination(label, style, *params) @destinations[label] = PDF::Writer::Object::Destination.new(self, @current_page, style, *params) end
Add content to the documents info object.
# File lib/pdf/writer.rb, line 1805 def add_info(label, value = 0) # This will only work if the label is one of the valid ones. Modify # this so that arrays can be passed as well. If @label is an array # then assume that it is key => value pairs else assume that they are # both scalar, anything else will probably error. if label.kind_of?(Hash) label.each { |kk, vv| @info.__send__(kk.downcase.intern, vv) } else @info.__send__(label.downcase.intern, value) end end
Add a link in the document to an internal destination (ie. within the document)
# File lib/pdf/writer.rb, line 681 def add_internal_link(label, x0, y0, x1, y1) PDF::Writer::Object::Annotation.new(self, :ilink, [x0, y0, x1, y1], label) end
Add a link in the document to an external URL.
# File lib/pdf/writer.rb, line 675 def add_link(uri, x0, y0, x1, y1) PDF::Writer::Object::Annotation.new(self, :link, [x0, y0, x1, y1], uri) end
After an object has been created, it will only show if it has been added, using this method.
# File lib/pdf/writer.rb, line 1778 def add_object(id, where = :this_page) obj = @loose_objects.detect { |ii| ii == id } if obj and @current_contents != obj case where when :all_pages, :this_page @add_loose_objects[obj] = where if where == :all_pages @current_contents.on_page.contents << obj if @current_contents.on_page when :even_pages @add_loose_objects[obj] = where page = @current_contents.on_page add_object(id) if (page.info.page_number % 2) == 0 when :odd_pages @add_loose_objects[obj] = where page = @current_contents.on_page add_object(id) if (page.info.page_number % 2) == 1 when :all_following_pages @add_loose_objects[obj] = :all_pages when :following_even_pages @add_loose_objects[obj] = :even_pages when :following_odd_pages @add_loose_objects[obj] = :odd_pages end end end
Add an outline item (Bookmark).
# File lib/pdf/writer.rb, line 686 def add_outline_item(label, title = label) PDF::Writer::Object::Outline.new(self, label, title) end
Add text
to the document at (x, y)
location at
size
and angle
. The
word_space_adjust
parameter is an internal parameter that
should not be used.
As of PDF::Writer 1.1, size
and
text
have been reversed and size
is now optional,
defaulting to the current font_size if unset.
# File lib/pdf/writer.rb, line 1362 def add_text(x, y, text, size = nil, angle = 0, word_space_adjust = 0) if text.kind_of?(Numeric) and size.kind_of?(String) text, size = size, text warn PDF::Writer::Lang[:add_text_parameters_reversed] % caller[0] end if size.nil? or size <= 0 size = @font_size end select_font("Helvetica") if @fonts.empty? text = text.to_s # If there are any open callbacks, then they should be called, to show # the start of the line @callbacks.reverse_each do |ii| info = ii.dup info[:x] = x info[:y] = y info[:angle] = angle info[:status] = :start_line info[:tag][self, info] end if angle == 0 add_content("\nBT %.3f %.3f Td" % [x, y]) else rad = PDF::Math.deg2rad(angle) tt = "\nBT %.3f %.3f %.3f %.3f %.3f %.3f Tm" tt = tt % [ Math.cos(rad), Math.sin(rad), -Math.sin(rad), Math.cos(rad), x, y ] add_content(tt) end if (word_space_adjust != 0) or not ((@word_space_adjust.nil?) and (@word_space_adjust != word_space_adjust)) @word_space_adjust = word_space_adjust add_content(" %.3f Tw" % word_space_adjust) end pos = -1 start = 0 loop do pos += 1 break if pos == text.size font_change = true tag_size, text, font_change = quick_text_tags(text, pos, font_change) if tag_size != 0 if pos > start part = text[start, pos - start] tt = " /F#{find_font(@current_font).font_id}" tt << " %.1f Tf %d Tr" % [ size, @current_text_render_style ] tt << " (#{PDF::Writer.escape(part)}) Tj" add_content(tt) end if font_change current_font! else add_content(" ET") xp = x yp = y tag_size, text, font_change, xp, yp = text_tags(text, pos, font_change, true, xp, yp, size, angle, word_space_adjust) # Restart the text object if angle.zero? add_content("\nBT %.3f %.3f Td" % [xp, yp]) else rad = PDF::Math.deg2rad(angle) tt = "\nBT %.3f %.3f %.3f %.3f %.3f %.3f Tm" tt = tt % [ Math.cos(rad), Math.sin(rad), -Math.sin(rad), Math.cos(rad), xp, yp ] add_content(tt) end if (word_space_adjust != 0) or (word_space_adjust != @word_space_adjust) @word_space_adjust = word_space_adjust add_content(" %.3f Tw" % [word_space_adjust]) end end pos += tag_size - 1 start = pos + 1 end end if start < text.size part = text[start..-1] tt = " /F#{find_font(@current_font).font_id}" tt << " %.1f Tf %d Tr" % [ size, @current_text_render_style ] tt << " (#{PDF::Writer.escape(part)}) Tj" add_content(tt) end add_content(" ET") # XXX: Experimental fix. @callbacks.reverse_each do |ii| info = ii.dup info[:x] = x info[:y] = y info[:angle] = angle info[:status] = :end_line info[:tag][self, info] end end
Add text to the page, but ensure that it fits within a certain width. If it
does not fit then put in as much as possible, breaking at word boundaries;
return the remainder. justification
and angle
can
also be specified for the text.
This will display the text; if it goes beyond the width width
,
it will backttrack to the previous space or hyphen and return the remainder
of the text.
justification
:left, :right, :center, or :full
# File lib/pdf/writer.rb, line 1604 def add_text_wrap(x, y, width, text, size = nil, justification = :left, angle = 0, test = false) if text.kind_of?(Numeric) and size.kind_of?(String) text, size = size, text warn PDF::Writer::Lang[:add_textw_parameters_reversed] % caller[0] end if size.nil? or size <= 0 size = @font_size end # Need to store the initial text state, as this will change during the # width calculation, but will need to be re-set before printing, so # that the chars work out right t_CTS = @current_text_state.dup select_font("Helvetica") if @fonts.empty? return "" if width <= 0 w = brk = brkw = 0 font = @current_font tw = width / size.to_f * 1000 pos = -1 loop do pos += 1 break if pos == text.size font_change = true tag_size, text, font_change = quick_text_tags(text, pos, font_change) if tag_size != 0 if font_change current_font! font = @current_font end pos += (tag_size - 1) else w += char_width(font, text[pos, 1]) if w > tw # We need to truncate this line if brk > 0 # There is somewhere to break the line. if text[brk] == " " tmp = text[0, brk] else tmp = text[0, brk + 1] end x, adjust = adjust_wrapped_text(tmp, brkw, width, x, justification) # Reset the text state @current_text_state = t_CTS.dup current_font! add_text(x, y, tmp, size, angle, adjust) unless test return text[brk + 1..-1] else # just break before the current character tmp = text[0, pos] # tmpw = (w - char_width(font, text[pos, 1])) * size / 1000.0 x, adjust = adjust_wrapped_text(tmp, brkw, width, x, justification) # Reset the text state @current_text_state = t_CTS.dup current_font! add_text(x, y, tmp, size, angle, adjust) unless test return text[pos..-1] end end if text[pos] == - brk = pos brkw = w * size / 1000.0 end if text[pos, 1] == " " brk = pos ctmp = text[pos] ctmp = @fonts[font].differences[ctmp] unless @fonts[font].differences.nil? z = @fonts[font].c[tmp].nil? ? 0 : @fonts[font].c[tmp]['WX'] brkw = (w - z) * size / 1000.0 end end end # There was no need to break this line. justification = :left if justification == :full tmpw = (w * size) / 1000.0 x, adjust = adjust_wrapped_text(text, tmpw, width, x, justification) # reset the text state @current_text_state = t_CTS.dup current_font! add_text(x, y, text, size, angle, adjust) unless test return "" end
Changes the insert_page property to append to the page set.
# File lib/pdf/writer.rb, line 2016 def append_page insert_mode(:last) end
Sets the bleed box area.
# File lib/pdf/writer.rb, line 657 def bleed_box(x0, y0, x1, y1) @pages.bleed_box = [ x0, y0, x1, y1 ] end
should be used for internal checks, not implemented as yet
# File lib/pdf/writer.rb, line 699 def check_all_here end
Close an object for writing.
# File lib/pdf/writer.rb, line 1762 def close_object unless @stack.empty? obj = @stack.pop @current_contents = obj[:contents] @current_page = obj[:page] end end
Convert a measurement in centimetres to points, which are the default PDF userspace units.
# File lib/pdf/writer.rb, line 245 def cm2pts(x) PDF::Writer.cm2pts(x) end
Indicates if columns are currently on.
# File lib/pdf/writer.rb, line 1902 def columns? @columns_on end
Returns true
if the document is compressed.
# File lib/pdf/writer.rb, line 438 def compressed? @compressed == true end
Selects the current font based on defined font families and the current text state. As noted in font_families, a “bi” font can be defined differently than an “ib” font. It should not be possible to have a “bb” text state, but if one were to show up, an entry for the font_families would have to be defined to select anything other than the default font. This function is to be called whenever the current text state is changed; it will update the current font to whatever the appropriate font defined in the font family.
When the user calls select_font, both the current base font and the current font will be reset; this function only changes the current font, not the current base font.
This will probably not be needed by end users.
# File lib/pdf/writer.rb, line 997 def current_font! select_font("Helvetica") unless @current_base_font font = File.basename(@current_base_font) if @font_families[font] and @font_families[font][@current_text_state] # Then we are in some state or another and this font has a family, # and the current setting exists within it select the font, then # return it. if File.dirname(@current_base_font) != '.' nf = File.join(File.dirname(@current_base_font), @font_families[font][@current_text_state]) else nf = @font_families[font][@current_text_state] end unless @fonts[nf] enc = { :encoding => @fonts[font].encoding, :differences => @fonts[font].differences } load_font(nf, enc) end @current_font = nf else @current_font = @current_base_font end end
Returns the current generic page number. This is based exclusively on the size of the page set.
# File lib/pdf/writer.rb, line 2111 def current_page_number @pageset.size end
Return the font descender, this will normally return a negative number. If you add this number to the baseline, you get the level of the bottom of the font it is in the PDF user units. Uses the current font_size if size is not provided.
# File lib/pdf/writer.rb, line 1047 def font_descender(size = nil) size = @font_size if size.nil? or size <= 0 select_font("Helvetica") if @fonts.empty? hi = @fonts[@current_font].fontbbox[1].to_f (size * hi / 1000.0) end
Return the height in units of the current font in the given size. Uses the current font_size if size is not provided.
# File lib/pdf/writer.rb, line 1035 def font_height(size = nil) size = @font_size if size.nil? or size <= 0 select_font("Helvetica") if @fonts.empty? hh = @fonts[@current_font].fontbbox[3].to_f - @fonts[@current_font].fontbbox[1].to_f (size * hh / 1000.0) end
Convert a measurement in inches to points, which are the default PDF userspace units.
# File lib/pdf/writer.rb, line 257 def in2pts(x) PDF::Writer.in2pts(x) end
Changes page insert mode. May be called as follows:
pdf.insert_mode # => current insert mode # The following four affect the insert mode without changing the # insert page or insert position. pdf.insert_mode(:on) # enables insert mode pdf.insert_mode(true) # enables insert mode pdf.insert_mode(:off) # disables insert mode pdf.insert_mode(false) # disables insert mode # Changes the insert mode, the insert page, and the insert # position at the same time. opts = { :on => true, :page => :last, :position => :before } pdf.insert_mode(opts)
# File lib/pdf/writer.rb, line 1980 def insert_mode(options = {}) case options when :on, true @insert_mode = true when :off, false @insert_mode = false else return @insert_mode unless options @insert_mode = options[:on] unless options[:on].nil? unless options[:page].nil? if @pageset[options[:page]].nil? or options[:page] == :last @insert_page = @pageset[-1] else @insert_page = @pageset[options[:page]] end end @insert_position = options[:position] if options[:position] end end
Returns or changes the insert page property.
pdf.insert_page # => current insert page pdf.insert_page(35) # insert at page 35 pdf.insert_page(:last) # insert at the last page
# File lib/pdf/writer.rb, line 2007 def insert_page(page = nil) return @insert_page unless page if page == :last @insert_page = @pageset[-1] else @insert_page = @pageset[page] end end
Returns or changes the insert position to be before or after the specified page.
pdf.insert_position # => current insert position pdf.insert_position(:before) # insert before #insert_page pdf.insert_position(:after) # insert before #insert_page
# File lib/pdf/writer.rb, line 2025 def insert_position(position = nil) return @insert_position unless position @insert_position = position end
Returns the estimated number of lines remaining given the default or specified font size.
# File lib/pdf/writer.rb, line 2446 def lines_remaining(font_size = nil) font_size ||= @font_size remaining = @y - @bottom_margin remaining / font_height(font_size).to_f end
Define the margins in centimetres.
# File lib/pdf/writer.rb, line 566 def margins_cm(top, left = top, bottom = top, right = left) margins_pt(cm2pts(top), cm2pts(left), cm2pts(bottom), cm2pts(right)) end
Define the margins in inches.
# File lib/pdf/writer.rb, line 571 def margins_in(top, left = top, bottom = top, right = left) margins_pt(in2pts(top), in2pts(left), in2pts(bottom), in2pts(right)) end
Define the margins in millimetres.
# File lib/pdf/writer.rb, line 561 def margins_mm(top, left = top, bottom = top, right = left) margins_pt(mm2pts(top), mm2pts(left), mm2pts(bottom), mm2pts(right)) end
Define the margins in points. This will move the y pointer
# T L B R pdf.margins_pt(36) # 36 36 36 36 pdf.margins_pt(36, 54) # 36 54 36 54 pdf.margins_pt(36, 54, 72) # 36 54 72 54 pdf.margins_pt(36, 54, 72, 90) # 36 54 72 90
# File lib/pdf/writer.rb, line 582 def margins_pt(top, left = top, bottom = top, right = left) # Set the margins to new values @top_margin = top @bottom_margin = bottom @left_margin = left @right_margin = right # Check to see if this means that the current writing position is # outside the writable area if @y > (@page_height - top) # Move y down @y = @page_height - top end start_new_page if @y < bottom # Make a new page end
Convert a measurement in millimetres to points, which are the default PDF userspace units.
# File lib/pdf/writer.rb, line 251 def mm2pts(x) PDF::Writer.mm2pts(x) end
Used to change the vertical position of the writing point. The pointer is
moved down the page by dy
(that is, y is reduced by dy
), so
if the pointer is to be moved up, a negative number must be used. Moving up
the page will not move to the previous page because of limitations in the
way that PDF::Writer works. The writing point
will be limited to the top margin position.
If make_space
is true and a new page is forced, then the
pointer will be moved down on the new page. This will allow space to be
reserved for graphics.
# File lib/pdf/writer.rb, line 550 def move_pointer(dy, make_space = false) @y -= dy if @y < @bottom_margin start_new_page @y -= dy if make_space elsif @y > absolute_top_margin @y = absolute_top_margin end end
Add a new page to the document. This also makes the new page the current active object. This allows for mandatory page creation regardless of multi-column output.
For most purposes, start_new_page is preferred.
# File lib/pdf/writer.rb, line 2084 def new_page(insert = false, page = nil, pos = :after) reset_state_at_page_finish if insert # The id from the PDF::Writer class is the id of the contents of the # page, not the page object itself. Query that object to find the # parent. _new_page = PDF::Writer::Object::Page.new(self, { :rpage => page, :pos => pos }) else _new_page = PDF::Writer::Object::Page.new(self) end reset_state_at_page_start # If there has been a stroke or fill color set, transfer them. fill_color! stroke_color! stroke_style! # the call to the page object set @current_contents to the present page, # so this can be returned as the page id # @current_contents _new_page end
Specify the Destination object where the document should open when it first
starts. style
must be one of the following values. The value
of style
affects the interpretation of params
.
Uses page
as the starting location.
# File lib/pdf/writer.rb, line 1829 def open_at(page, style, *params) d = PDF::Writer::Object::Destination.new(self, page, style, *params) @catalog.open_here = d end
Specify the Destination object where the document should open when it first
starts. style
must be one of the values detailed for
destinations. The value of style
affects the interpretation of
params
. Uses the current page as the starting location.
# File lib/pdf/writer.rb, line 1821 def open_here(style, *params) open_at(@current_page, style, *params) end
Opens a new PDF object for operating against. Returns the object’s identifier. To close the object, you’ll need to do:
ob = open_new_object # Opens the object # do stuff here close_object # Closes the PDF document # do stuff here reopen_object(ob) # Reopens the custom object. close_object # Closes it. restore_state # Returns full control to the PDF document.
… I think. I haven’t examined the full details to be sure of what this is doing, but the code works.
# File lib/pdf/writer.rb, line 2710 def open_new_object save_state oid = open_object close_object add_object(oid) reopen_object(oid) oid end
Make a loose object. The output will go into this object, until it is closed, then will revert to the current one. This object will not appear until it is included within a page. The function will return the object reference.
# File lib/pdf/writer.rb, line 1743 def open_object @stack << { :contents => @current_contents, :page => @current_page } @current_contents = PDF::Writer::Object::Contents.new(self) @loose_objects << @current_contents yield @current_contents if block_given? @current_contents end
Set the page mode of the catalog. Must be one of the following:
Neither document outline nor thumbnail images are visible.
Document outline visible.
Thumbnail images visible.
Full-screen mode, with no menu bar, window controls, or any other window visible.
Optional content group panel is visible.
# File lib/pdf/writer.rb, line 1869 def page_mode=(mode) @catalog.page_mode = value end
Return the PDF stream as a string.
# File lib/pdf/writer.rb, line 703 def render(debug = false) add_page_numbers @compression = false if $DEBUG or debug @arc4.init(@encryption_key) unless @arc4.nil? check_all_here xref = [] content = "%PDF-#{@version}\n%âãÏÓ\n" pos = content.size objects.each do |oo| cont = oo.to_s content << cont xref << pos pos += cont.size end # pos += 1 # Newline character before XREF content << "\nxref\n0 #{xref.size + 1}\n0000000000 65535 f \n" xref.each { |xx| content << "#{'%010d' % [xx]} 00000 n \n" } content << "\ntrailer\n" content << " << /Size #{xref.size + 1}\n" content << " /Root 1 0 R\n /Info #{@info.oid} 0 R\n" # If encryption has been applied to this document, then add the marker # for this dictionary if @arc4 and @encryption content << "/Encrypt #{@encryption.oid} 0 R\n" end if @file_identifier content << "/ID[<#{@file_identifier}><#{@file_identifier}>]\n" end content << " >>\nstartxref\n#{pos}\n%%EOF\n" content end
Opens an existing object for editing.
# File lib/pdf/writer.rb, line 1752 def reopen_object(id) @stack << { :contents => @current_contents, :page => @current_page } @current_contents = id # if this object is the primary contents for a page, then set the # current page to its parent @current_page = @current_contents.on_page unless @current_contents.on_page.nil? @current_contents end
Restore a previously saved state.
# File lib/pdf/writer.rb, line 1721 def restore_state unless @state_stack.empty? state = @state_stack.pop @current_fill_color = state.fill_color @current_stroke_color = state.stroke_color @current_text_render_style = state.text_render_style @current_stroke_style = state.stroke_style stroke_style! end add_content("\nQ") end
Save the PDF as a file to disk.
# File lib/pdf/writer.rb, line 2720 def save_as(name) File.open(name, "wb") { |f| f.write self.render } end
Saves the state.
# File lib/pdf/writer.rb, line 1695 def save_state PDF::Writer::State.new do |state| state.fill_color = @current_fill_color state.stroke_color = @current_stroke_color state.text_render_style = @current_text_render_style state.stroke_style = @current_stroke_style @state_stack.push state end add_content("\nq") end
If the named font
is not loaded, then load it and make the
required PDF objects to represent the font. If
the font is already loaded, then make it the current font.
The parameter encoding
applies only when the font is first
being loaded; it may not be applied later. It may either be an encoding
name or a hash. The Hash must contain two keys:
:encoding
The name of the encoding. Either none, WinAnsiEncoding, MacRomanEncoding, or MacExpertEncoding. For symbolic fonts, an encoding of none is recommended with a differences Hash.
:differences
This Hash value is a mapping between character byte values (0 .. 255) and character names from the AFM file for the font.
The standard PDF encodings are detailed fully in the PDF Reference version 1.6, Appendix D.
Note that WinAnsiEncoding is not the same as Windows code page 1252 (roughly equivalent to latin-1), Most characters map, but not all. The encoding value currently defaults to WinAnsiEncoding.
If the font’s “natural” encoding is desired, then it is necessary to
specify the encoding
parameter as { :encoding => nil
}
.
# File lib/pdf/writer.rb, line 975 def select_font(font, encoding = nil) load_font(font, encoding) unless @fonts[font] @current_base_font = font current_font! @current_base_font end
The number of PDF objects in the document
# File lib/pdf/writer.rb, line 137 def size @objects.size end
Starts multi-column output. Creates size
number of columns
with a gutter
PDF unit space between
each column.
If columns are already started, this will return false
.
# File lib/pdf/writer.rb, line 1910 def start_columns(size = 2, gutter = 10) # Start from the current y-position; make the set number of columns. return false if @columns_on @columns = { :current => 1, :bot_y => @y } @columns_on = true # store the current margins @columns[:left] = @left_margin @columns[:right] = @right_margin @columns[:top] = @top_margin @columns[:bottom] = @bottom_margin # Reset the margins to suit the new columns. Safe enough to assume the # first column here, but start from the current y-position. @top_margin = @page_height - @y @columns[:size] = size || 2 @columns[:gutter] = gutter || 10 w = absolute_right_margin - absolute_left_margin @columns[:width] = (w - ((size - 1) * gutter)) / size.to_f @right_margin = @page_width - (@left_margin + @columns[:width]) end
Creates a new page. If multi-column output is turned on, this will change
the column to the next greater or create a new page as necessary. If
force
is true, then a new page will be created even if
multi-column output is on.
# File lib/pdf/writer.rb, line 2034 def start_new_page(force = false) page_required = true if @columns_on # Check if this is just going to a new column. Increment the column # number. @columns[:current] += 1 if @columns[:current] <= @columns[:size] and not force page_required = false @columns[:bot_y] = @y if @y < @columns[:bot_y] else @columns[:current] = 1 @top_margin = @columns[:top] @columns[:bot_y] = absolute_top_margin end w = @columns[:width] g = @columns[:gutter] n = @columns[:current] - 1 @left_margin = @columns[:left] + n * (g + w) @right_margin = @page_width - (@left_margin + w) end if page_required or force # make a new page, setting the writing point back to the top. @y = absolute_top_margin # make the new page with a call to the basic class if @insert_mode id = new_page(true, @insert_page, @insert_position) @pageset << id # Manipulate the insert options so that inserted pages follow each # other @insert_page = id @insert_position = :after else @pageset << new_page end else @y = absolute_top_margin end @pageset end
Put page numbers on the pages from the current page. Place them relative to
the coordinates (x, y)
with the text horizontally relative
according to pos
, which may be :left
,
:right
, or :center
. The page numbers will be
written on each page using pattern
.
When pattern
is rendered, <PAGENUM> will be replaced
with the current page number; <TOTALPAGENUM> will be replaced with
the total number of pages in the page numbering scheme. The default
pattern
is “<PAGENUM> of <TOTALPAGENUM>”.
Each time page numbers are started, a new page number scheme will be started. The scheme number will be returned.
# File lib/pdf/writer.rb, line 2129 def start_page_numbering(x, y, size, pos = nil, pattern = nil, starting = nil) pos ||= :left pattern ||= "<PAGENUM> of <TOTALPAGENUM>" starting ||= 1 @page_numbering ||= [] @page_numbering << (o = {}) page = @pageset.size - 1 o[page] = { :x => x, :y => y, :pos => pos, :pattern => pattern, :starting => starting, :size => size, :start => true } @page_numbering.index(o) end
Turns off multi-column output. If we are in the first column, or the lowest point at which columns were written is higher than the bottom of the page, then the writing pointer will be placed at the lowest point. Otherwise, a new page will be started.
# File lib/pdf/writer.rb, line 1946 def stop_columns return false unless @columns_on @columns_on = false @columns[:bot_y] = @y if @y < @columns[:bot_y] if (@columns[:bot_y] > @bottom_margin) or @column_number == 1 @y = @columns[:bot_y] else start_new_page end restore_margins_after_columns @columns = {} true end
Stop an object from appearing on pages from this point on.
# File lib/pdf/writer.rb, line 1771 def stop_object(id) obj = @loose_objects.detect { |ii| ii.oid == id.oid } @add_loose_objects[obj] = nil end
Stop page numbering. Returns false
if page numbering is off.
If stop_total
is true, then then the totaling of pages for
this page numbering scheme
will be stopped as well. If
stop_at
is :current
, then the page numbering will
stop at this page; otherwise, it will stop at the next page.
This method has been dprecated.
# File lib/pdf/writer.rb, line 2187 def stop_page_numbering(stop_total = false, stop_at = :current, scheme = 0) return false unless @page_numbering page = @pageset.size - 1 @page_numbering[scheme][page] ||= {} o = @page_numbering[scheme][page] case [ stop_total, stop_at == :current ] when [ true, true ] o[:stop] = :stop_total when [ true, false ] o[:stop] = :stop_total_next when [ false, true ] o[:stop] = :stop_next else o[:stop] = :stop end end
This will add a string of text
to the document, starting at
the current drawing position. It will wrap to keep within the margins,
including optional offsets from the left and the right. The text will go to
the start of the next line when a return code “n” is found.
Possible options
are:
:font_size
The font size to be used. If not specified, is either the last font size or the default font size of 12 points. Setting this value changes the current font_size.
:left
number, gap to leave from the left margin
:right
number, gap to leave from the right margin
:absolute_left
number, absolute left position (overrides :left
)
:absolute_right
number, absolute right position (overrides :right
)
:justification
:left
, :right
, :center
,
:full
:leading
number, defines the total height taken by the line, independent of the font height.
:spacing
a Floating point number, though usually set to one of 1, 1.5, 2 (line spacing as used in word processing)
Only one of :leading
or :spacing
should be
specified (leading overrides spacing).
If the :test
option is true
, then this should
just check to see if the text is flowing onto a new page or not; returns
true
or false
. Note that the new page test is
only sensitive to exceeding the bottom margin of the page. It is not known
whether the writing of the text will require a new physical page or whether
it will require a new column.
# File lib/pdf/writer.rb, line 2334 def text(text, options = {}) # Apply the filtering which will make underlining (and other items) # function. text = preprocess_text(text) options ||= {} new_page_required = false __y = @y if options[:absolute_left] left = options[:absolute_left] else left = @left_margin left += options[:left] if options[:left] end if options[:absolute_right] right = options[:absolute_right] else right = absolute_right_margin right -= options[:right] if options[:right] end size = options[:font_size] || 0 if size <= 0 size = @font_size else @font_size = size end just = options[:justification] || :left if options[:leading] # leading instead of spacing height = options[:leading] elsif options[:spacing] height = options[:spacing] * font_height(size) else height = font_height(size) end text.each do |line| start = true loop do # while not line.empty? or start break if (line.nil? or line.empty?) and not start start = false @y -= height if @y < @bottom_margin if options[:test] new_page_required = true else # and then re-calc the left and right, in case they have # changed due to columns start_new_page @y -= height if options[:absolute_left] left = options[:absolute_left] else left = @left_margin left += options[:left] if options[:left] end if options[:absolute_right] right = options[:absolute_right] else right = absolute_right_margin right -= options[:right] if options[:right] end end end line = add_text_wrap(left, @y, right - left, line, size, just, 0, options[:test]) end end if options[:test] @y = __y new_page_required else @y end end
Calculate how wide a given text string will be on a page, at a given size.
This may be called externally, but is alse used by text_width. If size
is not specified, PDF::Writer will use the
current font_size.
The argument list is reversed from earlier versions.
# File lib/pdf/writer.rb, line 1489 def text_line_width(text, size = nil) if text.kind_of?(Numeric) and size.kind_of?(String) text, size = size, text warn PDF::Writer::Lang[:text_width_parameters_reversed] % caller[0] end if size.nil? or size <= 0 size = @font_size end # This function should not change any of the settings, though it will # need to track any tag which change during calculation, so copy them # at the start and put them back at the end. t_CTS = @current_text_state.dup select_font("Helvetica") if @fonts.empty? # converts a number or a float to a string so it can get the width tt = text.to_s # hmm, this is where it all starts to get tricky - use the font # information to calculate the width of each character, add them up # and convert to user units width = 0 font = @current_font pos = -1 loop do pos += 1 break if pos == tt.size font_change = true tag_size, text, font_change = quick_text_tags(text, pos, font_change) if tag_size != 0 if font_change current_font! font = @current_font end pos += tag_size - 1 else if "<" == tt[pos, 4] width += char_width(font, '<') pos += 3 elsif ">" == tt[pos, 4] width += char_width(font, '>') pos += 3 elsif "&" == tt[pos, 5] width += char_width(font, '&') pos += 4 else width += char_width(font, tt[pos, 1]) end end end @current_text_state = t_CTS.dup current_font! (width * size / 1000.0) end
Calculate how wide a given text string will be on a page, at a given size.
If size
is not specified, PDF::Writer will use the current font_size. The difference
between this method and text_line_width is that
this method will iterate over lines separated with newline characters.
The argument list is reversed from earlier versions.
# File lib/pdf/writer.rb, line 1554 def text_width(text, size = nil) if text.kind_of?(Numeric) and size.kind_of?(String) text, size = size, text warn PDF::Writer::Lang[:text_width_parameters_reversed] % caller[0] end if size.nil? or size <= 0 size = @font_size end max = 0 text.to_s.each do |line| width = text_line_width(line, size) max = width if width > max end max end
Sets the trim box area.
# File lib/pdf/writer.rb, line 652 def trim_box(x0, y0, x1, y1) @pages.trim_box = [ x0, y0, x1, y1 ] end
set the viewer preferences of the document, it is up to the browser to obey these.
# File lib/pdf/writer.rb, line 663 def viewer_preferences(label, value = 0) @catalog.viewer_preferences ||= PDF::Writer::Object::ViewerPreferences.new(self) # This will only work if the label is one of the valid ones. if label.kind_of?(Hash) label.each { |kk, vv| @catalog.viewer_preferences.__send__("#{kk.downcase}=".intern, vv) } else @catalog.viewer_preferences.__send__("#{label.downcase}=".intern, value) end end
Given a particular generic page number page_num
(numbered
sequentially from the beginning of the page set), return the page number
under a particular page numbering scheme
(defaults to the
first scheme turned on). Returns nil
if page numbering is not
turned on or if the page is not under the current numbering scheme.
This method has been dprecated.
# File lib/pdf/writer.rb, line 2157 def which_page_number(page_num, scheme = 0) return nil unless @page_numbering num = nil start = start_num = 1 @page_numbering[scheme].each do |kk, vv| if kk <= page_num if vv.kind_of?(Hash) unless vv[:starting].nil? start = vv[:starting] start_num = kk num = page_num - start_num + start end else num = nil end end end num end
Convert a measurement in centimetres to points, which are the default PDF userspace units.
# File lib/pdf/writer.rb, line 226 def cm2pts(x) (x / 2.54) * 72 end
Escape the text so that it’s safe for insertion into the PDF document.
# File lib/pdf/writer.rb, line 26 def self.escape(text) text.gsub(/\/, '\\\\'). gsub(/\(/, '\('). gsub(/\)/, '\)'). gsub(/</, '<'). gsub(/>/, '>'). gsub(/&/, '&') end
Convert a measurement in inches to points, which are the default PDF userspace units.
# File lib/pdf/writer.rb, line 238 def in2pts(x) x * 72 end
Convert a measurement in millimetres to points, which are the default PDF userspace units.
# File lib/pdf/writer.rb, line 232 def mm2pts(x) (x / 25.4) * 72 end
Creates a new PDF document as a writing canvas. It accepts three named parameters:
:paper
Specifies the size of the default page in PDF::Writer. This may be a four-element array of
coordinates specifying the lower-left (xll, yll)
and
upper-right (xur, yur)
corners, a two-element array of width
and height in centimetres, or a page name as defined in PAGE_SIZES.
:orientation
The orientation of the page, either long (:portrait) or wide (:landscape). This may be used to swap the width and the height of the page.
:version
The feature set available to the document is limited by the PDF version. Setting this version restricts the feature set available to PDF::Writer. PDF::Writer currently supports PDF version 1.3 features and does not yet support advanced features from PDF 1.4, 1.5, or 1.6.
# File lib/pdf/writer.rb, line 325 def initialize(options = {}) paper = options[:paper] || "LETTER" orientation = options[:orientation] || :portrait version = options[:version] || PDF_VERSION_13 @mutex = Mutex.new @current_id = @current_font_id = 0 # Start the document @objects = [] @callbacks = [] @font_families = {} @fonts = {} @stack = [] @state_stack = StateStack.new @loose_objects = [] @current_text_state = "" @options = {} @destinations = {} @add_loose_objects = {} @images = [] @word_space_adjust = nil @current_stroke_style = PDF::Writer::StrokeStyle.new(1) @page_numbering = nil @arc4 = nil @encryption = nil @file_identifier = nil @columns = {} @columns_on = false @insert_mode = nil @catalog = PDF::Writer::Object::Catalog.new(self) @outlines = PDF::Writer::Object::Outlines.new(self) @pages = PDF::Writer::Object::Pages.new(self) @current_node = @pages @procset = PDF::Writer::Object::Procset.new(self) @info = PDF::Writer::Object::Info.new(self) @page = PDF::Writer::Object::Page.new(self) @current_text_render_style = 0 @first_page = @page @version = version # Initialize the default font families. init_font_families @font_size = 10 @pageset = [@pages.first_page] if paper.kind_of?(Array) if paper.size == 4 size = paper # Coordinate Array else size = [0, 0, PDF::Writer.cm2pts(paper[0]), PDF::Writer.cm2pts(paper[1])] # Paper size in centimeters has been passed end else size = PAGE_SIZES[paper.upcase].dup end size[3], size[2] = size[2], size[3] if orientation == :landscape @pages.media_box = size @page_width = size[2] - size[0] @page_height = size[3] - size[1] @y = @page_height # Also set the margins to some reasonable defaults -- 1.27 cm, 36pt, # or 0.5 inches. margins_pt(36) # Set the current writing position to the top of the first page @y = absolute_top_margin # Get the ID of the page that was created during the instantiation # process. fill_color! Color::RGB::Black stroke_color! Color::RGB::Black yield self if block_given? end
Create the document with prepress options. Uses the same options as ::new (:paper
,
:orientation
, and :version
). It also supports the
following options:
:left_margin
The left margin.
:right_margin
The right margin.
:top_margin
The top margin.
:bottom_margin
The bottom margin.
:bleed_size
The size of the bleed area in points. Default 12.
:mark_length
The length of the prepress marks in points. Default 18.
The prepress marks are added to the loose objects and will appear on all pages.
# File lib/pdf/writer.rb, line 169 def prepress(options = { }) pdf = self.new(options) bleed_size = options[:bleed_size] || 12 mark_length = options[:mark_length] || 18 pdf.left_margin = options[:left_margin] if options[:left_margin] pdf.right_margin = options[:right_margin] if options[:right_margin] pdf.top_margin = options[:top_margin] if options[:top_margin] pdf.bottom_margin = options[:bottom_margin] if options[:bottom_margin] # This is in an "odd" order because the y-coordinate system in PDF # is from bottom to top. tx0 = pdf.pages.media_box[0] + pdf.left_margin ty0 = pdf.pages.media_box[3] - pdf.top_margin tx1 = pdf.pages.media_box[2] - pdf.right_margin ty1 = pdf.pages.media_box[1] + pdf.bottom_margin bx0 = tx0 - bleed_size by0 = ty0 - bleed_size bx1 = tx1 + bleed_size by1 = ty1 + bleed_size pdf.pages.trim_box = [ tx0, ty0, tx1, ty1 ] pdf.pages.bleed_box = [ bx0, by0, bx1, by1 ] all = pdf.open_object pdf.save_state kk = Color::CMYK.new(0, 0, 0, 100) pdf.stroke_color! kk pdf.fill_color! kk pdf.stroke_style! StrokeStyle.new(0.3) pdf.prepress_clip_mark(tx1, ty0, 0, mark_length, bleed_size) # Upper Right pdf.prepress_clip_mark(tx0, ty0, 90, mark_length, bleed_size) # Upper Left pdf.prepress_clip_mark(tx0, ty1, 180, mark_length, bleed_size) # Lower Left pdf.prepress_clip_mark(tx1, ty1, -90, mark_length, bleed_size) # Lower Right mid_x = pdf.pages.media_box[2] / 2.0 mid_y = pdf.pages.media_box[3] / 2.0 pdf.prepress_center_mark(mid_x, ty0, 0, mark_length, bleed_size) # Centre Top pdf.prepress_center_mark(tx0, mid_y, 90, mark_length, bleed_size) # Centre Left pdf.prepress_center_mark(mid_x, ty1, 180, mark_length, bleed_size) # Centre Bottom pdf.prepress_center_mark(tx1, mid_y, -90, mark_length, bleed_size) # Centre Right pdf.restore_state pdf.close_object pdf.add_object(all, :all) yield pdf if block_given? pdf end