Class PDF::Writer
In: lib/pdf/writer/state.rb
lib/pdf/writer.rb
Parent: Object
Transaction::Simple SimpleTable TechBook Complex Action Procset FontDescriptor FontEncoding Destination Info Catalog Encryption Contents Pages Outline Outlines Annotation Page Font ViewerPreferences Image Hash OHash QuickRef FontMetrics ARC4 StrokeStyle PolygonPoint ImageInfo StdDev lib/pdf/simpletable.rb lib/pdf/techbook.rb lib/pdf/writer.rb lib/pdf/quickref.rb Math lib/pdf/writer/fontmetrics.rb lib/pdf/writer/ohash.rb lib/pdf/writer/arc4.rb lib/pdf/writer/strokestyle.rb lib/pdf/writer/graphics.rb lib/pdf/writer/object.rb lib/pdf/writer/object/image.rb External EN Lang OffsetReader lib/pdf/writer/graphics/imageinfo.rb Graphics lib/pdf/writer/object/outlines.rb lib/pdf/writer/object/destination.rb lib/pdf/writer/object/viewerpreferences.rb lib/pdf/writer/object/fontencoding.rb lib/pdf/writer/object/page.rb lib/pdf/writer/object/contents.rb lib/pdf/writer/object/procset.rb lib/pdf/writer/object/pages.rb lib/pdf/writer/object/info.rb lib/pdf/writer/object/encryption.rb lib/pdf/writer/object/catalog.rb lib/pdf/writer/object/outline.rb lib/pdf/writer/object/fontdescriptor.rb lib/pdf/writer/object/action.rb lib/pdf/writer/object/font.rb lib/pdf/writer/object/annotation.rb Object Writer lib/pdf/charts/stddev.rb Charts PDF dot/m_33_0.png

Methods

Included Modules

PDF::Writer::Graphics Transaction::Simple

Classes and Modules

Class PDF::Writer::State
Class PDF::Writer::StateStack
Class PDF::Writer::TagAlink
Class PDF::Writer::TagBullet
Class PDF::Writer::TagDisc
Class PDF::Writer::TagIlink
Class PDF::Writer::TagUline

Constants

VERSION = '1.1.7'   The version of PDF::Writer.
FONT_PATH = []   The system font path. The sytem font path will be determined differently for each operating system.
Win32:Uses ENV[‘SystemRoot’]/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.
OS X:The fonts are found in /System/Library/Fonts.
Linux: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.
PAGE_SIZES = { # :value {...}: "4A0" => [0, 0, 4767.87, 6740.79], "2A0" => [0, 0, 3370.39, 4767.87], "A0" => [0, 0, 2383.94, 3370.39], "A1" => [0, 0, 1683.78, 2383.94], "A2" => [0, 0, 1190.55, 1683.78], "A3" => [0, 0, 841.89, 1190.55], "A4" => [0, 0, 595.28, 841.89], "A5" => [0, 0, 419.53, 595.28], "A6" => [0, 0, 297.64, 419.53], "A7" => [0, 0, 209.76, 297.64], "A8" => [0, 0, 147.40, 209.76], "A9" => [0, 0, 104.88, 147.40], "A10" => [0, 0, 73.70, 104.88], "B0" => [0, 0, 2834.65, 4008.19], "B1" => [0, 0, 2004.09, 2834.65], "B2" => [0, 0, 1417.32, 2004.09], "B3" => [0, 0, 1000.63, 1417.32], "B4" => [0, 0, 708.66, 1000.63], "B5" => [0, 0, 498.90, 708.66], "B6" => [0, 0, 354.33, 498.90], "B7" => [0, 0, 249.45, 354.33], "B8" => [0, 0, 175.75, 249.45], "B9" => [0, 0, 124.72, 175.75], "B10" => [0, 0, 87.87, 124.72], "C0" => [0, 0, 2599.37, 3676.54], "C1" => [0, 0, 1836.85, 2599.37], "C2" => [0, 0, 1298.27, 1836.85], "C3" => [0, 0, 918.43, 1298.27], "C4" => [0, 0, 649.13, 918.43], "C5" => [0, 0, 459.21, 649.13], "C6" => [0, 0, 323.15, 459.21], "C7" => [0, 0, 229.61, 323.15], "C8" => [0, 0, 161.57, 229.61], "C9" => [0, 0, 113.39, 161.57], "C10" => [0, 0, 79.37, 113.39], "RA0" => [0, 0, 2437.80, 3458.27], "RA1" => [0, 0, 1729.13, 2437.80], "RA2" => [0, 0, 1218.90, 1729.13], "RA3" => [0, 0, 864.57, 1218.90], "RA4" => [0, 0, 609.45, 864.57], "SRA0" => [0, 0, 2551.18, 3628.35], "SRA1" => [0, 0, 1814.17, 2551.18], "SRA2" => [0, 0, 1275.59, 1814.17], "SRA3" => [0, 0, 907.09, 1275.59], "SRA4" => [0, 0, 637.80, 907.09], "LETTER" => [0, 0, 612.00, 792.00], "LEGAL" => [0, 0, 612.00, 1008.00], "FOLIO" => [0, 0, 612.00, 936.00], "EXECUTIVE" => [0, 0, 521.86, 756.00]   Standard page size names. One of these may be provided to PDF::Writer.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
PDF_VERSION_13 = '1.3'
PDF_VERSION_14 = '1.4'
PDF_VERSION_15 = '1.5'
PDF_VERSION_16 = '1.6'
ENCRYPT_OPTIONS = { #:nodoc: :print => 4, :modify => 8, :copy => 16, :add => 32   Standard encryption/DRM options.
TAGS = { :pair => { }, :single => { }, :replace => { }   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>.

Attributes

absolute_bottom_margin  [R]  Returns the absolute y position of the bottom margin.
absolute_left_margin  [R]  The absolute x position of the left margin.
absolute_right_margin  [R]  The absolute x position of the right margin.
absolute_top_margin  [R]  Returns the absolute y position of the top margin.
absolute_x_middle  [R]  The absolute x middle position.
absolute_y_middle  [R]  The absolute y middle position.
bottom_margin  [RW] 
column_count  [R]  The total number of columns. Returns zero (0) if columns are off.
column_gutter  [R]  The gutter between columns. This will return zero (0) if columns are off.
column_number  [R]  The current column number. Returns zero (0) if columns are off.
column_width  [R]  The width of the currently active column. This will return zero (0) if columns are off.
compressed  [RW]  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.
current_base_font  [R] 
current_contents  [R]  Returns the current contents object to which raw PDF instructions may be written.
current_font  [R] 
encryption_key  [RW]  The string that will be used to encrypt this PDF document.
first_page  [R]  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.
font_families  [R]  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.

font_size  [RW] 
info  [R]  The PDF::Writer::Object::Info info object. This is used to provide certain metadata.
left_margin  [RW] 
margin_height  [R]  The height of the margin area.
margin_width  [R]  The width of the margin area.
margin_x_middle  [R]  The middle of the writing area between the left and right margins.
margin_y_middle  [R]  The middle of the writing area between the top and bottom margins.
page_height  [R] 
page_width  [R] 
pointer  [RW]  The vertical position of the writing point. If the vertical position is outside of the bottom margin, a new page will be created.
right_margin  [RW] 
top_margin  [RW] 
version  [R]  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.
y  [RW]  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.

Public Class methods

Convert a measurement in centimetres to points, which are the default PDF userspace units.

[Source]

     # File lib/pdf/writer.rb, line 226
226:     def cm2pts(x)
227:       (x / 2.54) * 72
228:     end

Escape the text so that it‘s safe for insertion into the PDF document.

[Source]

    # File lib/pdf/writer.rb, line 26
26:     def self.escape(text)
27:       text.gsub(/\\/, '\\\\\\\\').
28:            gsub(/\(/, '\\(').
29:            gsub(/\)/, '\\)').
30:            gsub(/&lt;/, '<').
31:            gsub(/&gt;/, '>').
32:            gsub(/&amp;/, '&')
33:     end

Convert a measurement in inches to points, which are the default PDF userspace units.

[Source]

     # File lib/pdf/writer.rb, line 238
238:     def in2pts(x)
239:       x * 72
240:     end

Convert a measurement in millimetres to points, which are the default PDF userspace units.

[Source]

     # File lib/pdf/writer.rb, line 232
232:     def mm2pts(x)
233:       (x / 25.4) * 72
234:     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.

[Source]

     # File lib/pdf/writer.rb, line 325
325:   def initialize(options = {})
326:     paper       = options[:paper] || "LETTER"
327:     orientation = options[:orientation] || :portrait
328:     version     = options[:version] || PDF_VERSION_13
329: 
330:     @mutex = Mutex.new
331:     @current_id = @current_font_id = 0
332: 
333:       # Start the document
334:     @objects              = []
335:     @callbacks            = []
336:     @font_families        = {}
337:     @fonts                = {}
338:     @stack                = []
339:     @state_stack          = StateStack.new
340:     @loose_objects        = []
341:     @current_text_state   = ""
342:     @options              = {}
343:     @destinations         = {}
344:     @add_loose_objects    = {}
345:     @images               = []
346:     @word_space_adjust    = nil
347:     @current_stroke_style = PDF::Writer::StrokeStyle.new(1)
348:     @page_numbering       = nil
349:     @arc4                 = nil
350:     @encryption           = nil
351:     @file_identifier      = nil
352: 
353:     @columns              = {}
354:     @columns_on           = false
355:     @insert_mode          = nil
356: 
357:     @catalog  = PDF::Writer::Object::Catalog.new(self)
358:     @outlines = PDF::Writer::Object::Outlines.new(self)
359:     @pages    = PDF::Writer::Object::Pages.new(self)
360: 
361:     @current_node       = @pages
362:     @procset  = PDF::Writer::Object::Procset.new(self)
363:     @info     = PDF::Writer::Object::Info.new(self)
364:     @page     = PDF::Writer::Object::Page.new(self)
365:     @current_text_render_style  = 0
366:     @first_page     = @page
367: 
368:     @version        = version
369: 
370:       # Initialize the default font families.
371:     init_font_families
372: 
373:     @font_size = 10
374:     @pageset = [@pages.first_page]
375: 
376:     if paper.kind_of?(Array)
377:       if paper.size == 4
378:         size = paper # Coordinate Array
379:       else
380:         size = [0, 0, PDF::Writer.cm2pts(paper[0]), PDF::Writer.cm2pts(paper[1])]
381:           # Paper size in centimeters has been passed
382:       end
383:     else
384:       size = PAGE_SIZES[paper.upcase].dup
385:     end
386:     size[3], size[2] = size[2], size[3] if orientation == :landscape
387: 
388:     @pages.media_box  = size
389: 
390:     @page_width       = size[2] - size[0]
391:     @page_height      = size[3] - size[1]
392:     @y = @page_height
393: 
394:       # Also set the margins to some reasonable defaults -- 1.27 cm, 36pt,
395:       # or 0.5 inches.
396:     margins_pt(36)
397: 
398:       # Set the current writing position to the top of the first page
399:     @y = absolute_top_margin
400:       # Get the ID of the page that was created during the instantiation
401:       # process.
402: 
403:     fill_color!   Color::RGB::Black
404:     stroke_color! Color::RGB::Black
405: 
406:     yield self if block_given?
407:   end

Create the document with prepress options. Uses the same options as PDF::Writer.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.

[Source]

     # File lib/pdf/writer.rb, line 169
169:     def prepress(options = { })
170:       pdf = self.new(options)
171: 
172:       bleed_size  = options[:bleed_size] || 12
173:       mark_length = options[:mark_length] || 18
174: 
175:       pdf.left_margin   = options[:left_margin] if options[:left_margin]
176:       pdf.right_margin  = options[:right_margin] if options[:right_margin]
177:       pdf.top_margin    = options[:top_margin] if options[:top_margin]
178:       pdf.bottom_margin = options[:bottom_margin] if options[:bottom_margin]
179: 
180:       # This is in an "odd" order because the y-coordinate system in PDF
181:       # is from bottom to top.
182:       tx0 = pdf.pages.media_box[0] + pdf.left_margin
183:       ty0 = pdf.pages.media_box[3] - pdf.top_margin
184:       tx1 = pdf.pages.media_box[2] - pdf.right_margin
185:       ty1 = pdf.pages.media_box[1] + pdf.bottom_margin
186: 
187:       bx0 = tx0 - bleed_size
188:       by0 = ty0 - bleed_size
189:       bx1 = tx1 + bleed_size
190:       by1 = ty1 + bleed_size
191: 
192:       pdf.pages.trim_box  = [ tx0, ty0, tx1, ty1 ]
193:       pdf.pages.bleed_box = [ bx0, by0, bx1, by1 ]
194: 
195:       all = pdf.open_object
196:       pdf.save_state
197:       kk = Color::CMYK.new(0, 0, 0, 100)
198:       pdf.stroke_color! kk
199:       pdf.fill_color! kk
200:       pdf.stroke_style! StrokeStyle.new(0.3)
201: 
202:       pdf.prepress_clip_mark(tx1, ty0,   0, mark_length, bleed_size)  # Upper Right
203:       pdf.prepress_clip_mark(tx0, ty0,  90, mark_length, bleed_size)  # Upper Left
204:       pdf.prepress_clip_mark(tx0, ty1, 180, mark_length, bleed_size)  # Lower Left
205:       pdf.prepress_clip_mark(tx1, ty1, -90, mark_length, bleed_size)  # Lower Right
206: 
207:       mid_x = pdf.pages.media_box[2] / 2.0
208:       mid_y = pdf.pages.media_box[3] / 2.0
209: 
210:       pdf.prepress_center_mark(mid_x, ty0,   0, mark_length, bleed_size) # Centre Top
211:       pdf.prepress_center_mark(tx0, mid_y,  90, mark_length, bleed_size) # Centre Left
212:       pdf.prepress_center_mark(mid_x, ty1, 180, mark_length, bleed_size) # Centre Bottom
213:       pdf.prepress_center_mark(tx1, mid_y, -90, mark_length, bleed_size) # Centre Right
214: 
215:       pdf.restore_state
216:       pdf.close_object
217:       pdf.add_object(all, :all)
218: 
219:       yield pdf if block_given?
220: 
221:       pdf
222:     end

Private Class methods

Parse the fonts.conf XML file.

[Source]

     # File lib/pdf/writer.rb, line 94
 94:     def parse_fonts_conf(filename)
 95:       doc = REXML::Document.new(File.open(filename, "rb")).root rescue nil
 96: 
 97:       if doc
 98:         path = REXML::XPath.match(doc, '//dir').map do |el|
 99:           el.text.gsub($/, '')
100:         end
101:         doc = nil
102:       else
103:         path = []
104:       end
105:       path
106:     end

Public Instance methods

memory improvement for transaction-simple

[Source]

      # File lib/pdf/writer.rb, line 2754
2754:   def _post_transaction_rewind
2755:     @objects.each { |e| e.instance_variable_set(:@parent,self) }
2756:   end

add content to the currently active object

[Source]

      # File lib/pdf/writer.rb, line 1058
1058:   def add_content(cc)
1059:     @current_contents << cc
1060:   end

Create a labelled destination within the document. The label is the name which will be used for <c:ilink> destinations.

XYZ: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:Fit the page to the viewport (horizontal and vertical). params will be ignored.
FitH:Fit the page horizontally to the viewport. The top of the viewport is set to the first value in params.
FitV:Fit the page vertically to the viewport. The left of the viewport is set to the first value in params.
FitR:Fits the page to the provided rectangle. params must have four values representing the left, bottom, right, and top positions, respectively.
FitB:Fits the page to the bounding box of the page. params is ignored.
FitBH:Fits the page horizontally to the bounding box of the page. The top position is defined by the first value in params.
FitBV:Fits the page vertically to the bounding box of the page. The left position is defined by the first value in params.

[Source]

      # File lib/pdf/writer.rb, line 1885
1885:   def add_destination(label, style, *params)
1886:     @destinations[label] = PDF::Writer::Object::Destination.new(self, @current_page, style, *params)
1887:   end

Add content to the documents info object.

[Source]

      # File lib/pdf/writer.rb, line 1834
1834:   def add_info(label, value = 0)
1835:       # This will only work if the label is one of the valid ones. Modify
1836:       # this so that arrays can be passed as well. If @label is an array
1837:       # then assume that it is key => value pairs else assume that they are
1838:       # both scalar, anything else will probably error.
1839:     if label.kind_of?(Hash)
1840:       label.each { |kk, vv| @info.__send__(kk.downcase.intern, vv) }
1841:     else
1842:       @info.__send__(label.downcase.intern, value)
1843:     end
1844:   end

Add a link in the document to an internal destination (ie. within the document)

[Source]

     # File lib/pdf/writer.rb, line 681
681:   def add_internal_link(label, x0, y0, x1, y1)
682:     PDF::Writer::Object::Annotation.new(self, :ilink, [x0, y0, x1, y1], label)
683:   end

Add a link in the document to an external URL.

[Source]

     # File lib/pdf/writer.rb, line 675
675:   def add_link(uri, x0, y0, x1, y1)
676:     PDF::Writer::Object::Annotation.new(self, :link, [x0, y0, x1, y1], uri)
677:   end

After an object has been created, it will only show if it has been added, using this method.

[Source]

      # File lib/pdf/writer.rb, line 1807
1807:   def add_object(id, where = :this_page)
1808:     obj = @loose_objects.detect { |ii| ii == id }
1809: 
1810:     if obj and @current_contents != obj
1811:       case where
1812:       when :all_pages, :this_page
1813:         @add_loose_objects[obj] = where if where == :all_pages
1814:         @current_contents.on_page.contents << obj if @current_contents.on_page
1815:       when :even_pages
1816:         @add_loose_objects[obj] = where
1817:         page = @current_contents.on_page
1818:         add_object(id) if (page.info.page_number % 2) == 0
1819:       when :odd_pages
1820:         @add_loose_objects[obj] = where
1821:         page = @current_contents.on_page
1822:         add_object(id) if (page.info.page_number % 2) == 1
1823:       when :all_following_pages
1824:         @add_loose_objects[obj] = :all_pages
1825:       when :following_even_pages
1826:         @add_loose_objects[obj] = :even_pages
1827:       when :following_odd_pages
1828:         @add_loose_objects[obj] = :odd_pages
1829:       end
1830:     end
1831:   end

Add an outline item (Bookmark).

[Source]

     # File lib/pdf/writer.rb, line 686
686:   def add_outline_item(label, title = label)
687:     PDF::Writer::Object::Outline.new(self, label, title)
688:   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.

[Source]

      # File lib/pdf/writer.rb, line 1391
1391:   def add_text(x, y, text, size = nil, angle = 0, word_space_adjust = 0)
1392:     if text.kind_of?(Numeric) and size.kind_of?(String)
1393:       text, size = size, text
1394:       warn PDF::Writer::Lang[:add_text_parameters_reversed] % caller[0]
1395:     end
1396: 
1397:     if size.nil? or size <= 0
1398:       size = @font_size
1399:     end
1400: 
1401:     select_font("Helvetica") if @fonts.empty?
1402: 
1403:     text = text.to_s
1404: 
1405:       # If there are any open callbacks, then they should be called, to show
1406:       # the start of the line
1407:     @callbacks.reverse_each do |ii|
1408:       info = ii.dup
1409:       info[:x]      = x
1410:       info[:y]      = y
1411:       info[:angle]  = angle
1412:       info[:status] = :start_line
1413: 
1414:       info[:tag][self, info]
1415:     end
1416:     if angle == 0
1417:       add_content("\nBT %.3f %.3f Td" % [x, y])
1418:     else
1419:       rad = PDF::Math.deg2rad(angle)
1420:       tt = "\nBT %.3f %.3f %.3f %.3f %.3f %.3f Tm"
1421:       tt = tt % [ Math.cos(rad), Math.sin(rad), -Math.sin(rad), Math.cos(rad), x, y ]
1422:       add_content(tt)
1423:     end
1424: 
1425:     if (word_space_adjust != 0) or not ((@word_space_adjust.nil?) and (@word_space_adjust != word_space_adjust))
1426:       @word_space_adjust = word_space_adjust
1427:       add_content(" %.3f Tw" % word_space_adjust)
1428:     end
1429: 
1430:     pos = -1
1431:     start = 0
1432:     loop do
1433:       pos += 1
1434:       break if pos == text.size
1435:       font_change = true
1436:       tag_size, text, font_change = quick_text_tags(text, pos, font_change)
1437: 
1438:       if tag_size != 0
1439:         if pos > start
1440:           part = text[start, pos - start]
1441:           tt = " /F#{find_font(@current_font).font_id}"
1442:           tt << " %.1f Tf %d Tr" % [ size, @current_text_render_style ]
1443:           tt << " (#{PDF::Writer.escape(part)}) Tj"
1444:           add_content(tt)
1445:         end
1446: 
1447:         if font_change
1448:           current_font!
1449:         else
1450:           add_content(" ET")
1451:           xp = x
1452:           yp = y
1453:           tag_size, text, font_change, xp, yp = text_tags(text, pos, font_change, true, xp, yp, size, angle, word_space_adjust)
1454: 
1455:             # Restart the text object
1456:           if angle.zero?
1457:             add_content("\nBT %.3f %.3f Td" % [xp, yp])
1458:           else
1459:             rad = PDF::Math.deg2rad(angle)
1460:             tt = "\nBT %.3f %.3f %.3f %.3f %.3f %.3f Tm"
1461:             tt = tt % [ Math.cos(rad), Math.sin(rad), -Math.sin(rad), Math.cos(rad), xp, yp ]
1462:             add_content(tt)
1463:           end
1464: 
1465:           if (word_space_adjust != 0) or (word_space_adjust != @word_space_adjust)
1466:             @word_space_adjust = word_space_adjust
1467:             add_content(" %.3f Tw" % [word_space_adjust])
1468:           end
1469:         end
1470: 
1471:         pos += tag_size - 1
1472:         start = pos + 1
1473:       end
1474:     end
1475: 
1476:     if start < text.size
1477:       part = text[start..-1]
1478: 
1479:       tt = " /F#{find_font(@current_font).font_id}"
1480:       tt << " %.1f Tf %d Tr" % [ size, @current_text_render_style ]
1481:       tt << " (#{PDF::Writer.escape(part)}) Tj"
1482:       add_content(tt)
1483:     end
1484:     add_content(" ET")
1485: 
1486:       # XXX: Experimental fix.
1487:     @callbacks.reverse_each do |ii|
1488:       info = ii.dup
1489:       info[:x]      = x
1490:       info[:y]      = y
1491:       info[:angle]  = angle
1492:       info[:status] = :end_line
1493:       info[:tag][self, info]
1494:     end
1495:   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

[Source]

      # File lib/pdf/writer.rb, line 1633
1633:   def add_text_wrap(x, y, width, text, size = nil, justification = :left, angle = 0, test = false)
1634:     if text.kind_of?(Numeric) and size.kind_of?(String)
1635:       text, size = size, text
1636:       warn PDF::Writer::Lang[:add_textw_parameters_reversed] % caller[0]
1637:     end
1638: 
1639:     if size.nil? or size <= 0
1640:       size = @font_size
1641:     end
1642: 
1643:       # Need to store the initial text state, as this will change during the
1644:       # width calculation, but will need to be re-set before printing, so
1645:       # that the chars work out right
1646:     t_CTS = @current_text_state.dup
1647: 
1648:     select_font("Helvetica") if @fonts.empty?
1649:     return "" if width <= 0
1650: 
1651:     w = brk = brkw = 0
1652:     font = @current_font
1653:     tw = width / size.to_f * 1000
1654: 
1655:     pos = -1
1656:     loop do
1657:       pos += 1
1658:       break if pos == text.size
1659:       font_change = true
1660:       tag_size, text, font_change = quick_text_tags(text, pos, font_change)
1661:       if tag_size != 0
1662:         if font_change
1663:           current_font!
1664:           font = @current_font
1665:         end
1666:         pos += (tag_size - 1)
1667:       else
1668:         w += char_width(font, text[pos, 1])
1669: 
1670:         if w > tw # We need to truncate this line
1671:           if brk > 0 # There is somewhere to break the line.
1672:             if text[brk] == " "
1673:               tmp = text[0, brk]
1674:             else
1675:               tmp = text[0, brk + 1]
1676:             end
1677:             x, adjust = adjust_wrapped_text(tmp, brkw, width, x, justification)
1678: 
1679:               # Reset the text state
1680:             @current_text_state = t_CTS.dup
1681:             current_font!
1682:             add_text(x, y, tmp, size, angle, adjust) unless test
1683:             return text[brk + 1..-1]
1684:           else # just break before the current character
1685:             tmp = text[0, pos]
1686: #           tmpw = (w - char_width(font, text[pos, 1])) * size / 1000.0
1687:             x, adjust = adjust_wrapped_text(tmp, brkw, width, x, justification)
1688: 
1689:               # Reset the text state
1690:             @current_text_state = t_CTS.dup
1691:             current_font!
1692:             add_text(x, y, tmp, size, angle, adjust) unless test
1693:             return text[pos..-1]
1694:           end
1695:         end
1696: 
1697:         if text[pos] == ?-
1698:           brk = pos
1699:           brkw = w * size / 1000.0
1700:         end
1701: 
1702:         if text[pos, 1] == " "
1703:           brk = pos
1704:           ctmp = text[pos]
1705:           ctmp = @fonts[font].differences[ctmp] unless @fonts[font].differences.nil?
1706:           z = @fonts[font].c[tmp].nil? ? 0 : @fonts[font].c[tmp]['WX']
1707:           brkw = (w - z) * size / 1000.0
1708:         end
1709:       end
1710:     end
1711: 
1712:       # There was no need to break this line.
1713:     justification = :left if justification == :full
1714:     tmpw = (w * size) / 1000.0
1715:     x, adjust = adjust_wrapped_text(text, tmpw, width, x, justification)
1716:       # reset the text state
1717:     @current_text_state = t_CTS.dup
1718:     current_font!
1719:     add_text(x, y, text, size, angle, adjust) unless test
1720:     return ""
1721:   end

Changes the insert_page property to append to the page set.

[Source]

      # File lib/pdf/writer.rb, line 2045
2045:   def append_page
2046:     insert_mode(:last)
2047:   end

Sets the bleed box area.

[Source]

     # File lib/pdf/writer.rb, line 657
657:   def bleed_box(x0, y0, x1, y1)
658:     @pages.bleed_box = [ x0, y0, x1, y1 ]
659:   end

should be used for internal checks, not implemented as yet

[Source]

     # File lib/pdf/writer.rb, line 699
699:   def check_all_here
700:   end

Close an object for writing.

[Source]

      # File lib/pdf/writer.rb, line 1791
1791:   def close_object
1792:     unless @stack.empty?
1793:       obj = @stack.pop
1794:       @current_contents = obj[:contents]
1795:       @current_page = obj[:page]
1796:     end
1797:   end

Convert a measurement in centimetres to points, which are the default PDF userspace units.

[Source]

     # File lib/pdf/writer.rb, line 245
245:   def cm2pts(x)
246:     PDF::Writer.cm2pts(x)
247:   end

Indicates if columns are currently on.

[Source]

      # File lib/pdf/writer.rb, line 1931
1931:   def columns?
1932:     @columns_on
1933:   end

Returns true if the document is compressed.

[Source]

     # File lib/pdf/writer.rb, line 438
438:   def compressed?
439:     @compressed == true
440:   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.

[Source]

      # File lib/pdf/writer.rb, line 1026
1026:   def current_font!
1027:     select_font("Helvetica") unless @current_base_font
1028: 
1029:     font = File.basename(@current_base_font)
1030:     if @font_families[font] and @font_families[font][@current_text_state]
1031:         # Then we are in some state or another and this font has a family,
1032:         # and the current setting exists within it select the font, then
1033:         # return it.
1034:       if File.dirname(@current_base_font) != '.'
1035:         nf = File.join(File.dirname(@current_base_font), @font_families[font][@current_text_state])
1036:       else
1037:         nf = @font_families[font][@current_text_state]
1038:       end
1039: 
1040:       unless @fonts[nf]
1041:         enc = {
1042:           :encoding     => @fonts[font].encoding,
1043:           :differences  => @fonts[font].differences
1044:         }
1045:         load_font(nf, enc)
1046:       end
1047:       @current_font = nf
1048:     else
1049:       @current_font = @current_base_font
1050:     end
1051:   end

Returns the current generic page number. This is based exclusively on the size of the page set.

[Source]

      # File lib/pdf/writer.rb, line 2140
2140:   def current_page_number
2141:     @pageset.size
2142:   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.

[Source]

      # File lib/pdf/writer.rb, line 1076
1076:   def font_descender(size = nil)
1077:     size = @font_size if size.nil? or size <= 0
1078: 
1079:     select_font("Helvetica") if @fonts.empty?
1080:     hi = @fonts[@current_font].fontbbox[1].to_f
1081:     (size * hi / 1000.0)
1082:   end

Return the height in units of the current font in the given size. Uses the current font_size if size is not provided.

[Source]

      # File lib/pdf/writer.rb, line 1064
1064:   def font_height(size = nil)
1065:     size = @font_size if size.nil? or size <= 0
1066: 
1067:     select_font("Helvetica") if @fonts.empty?
1068:     hh = @fonts[@current_font].fontbbox[3].to_f - @fonts[@current_font].fontbbox[1].to_f
1069:     (size * hh / 1000.0)
1070:   end

Convert a measurement in inches to points, which are the default PDF userspace units.

[Source]

     # File lib/pdf/writer.rb, line 257
257:   def in2pts(x)
258:     PDF::Writer.in2pts(x)
259:   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)

[Source]

      # File lib/pdf/writer.rb, line 2009
2009:   def insert_mode(options = {})
2010:     case options
2011:     when :on, true
2012:       @insert_mode = true
2013:     when :off, false
2014:       @insert_mode = false
2015:     else
2016:       return @insert_mode unless options
2017: 
2018:       @insert_mode = options[:on] unless options[:on].nil?
2019: 
2020:       unless options[:page].nil?
2021:         if @pageset[options[:page]].nil? or options[:page] == :last
2022:           @insert_page = @pageset[-1]
2023:         else
2024:           @insert_page = @pageset[options[:page]]
2025:         end
2026:       end
2027: 
2028:       @insert_position = options[:position] if options[:position]
2029:     end
2030:   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

[Source]

      # File lib/pdf/writer.rb, line 2036
2036:   def insert_page(page = nil)
2037:     return @insert_page unless page
2038:     if page == :last
2039:       @insert_page = @pageset[-1]
2040:     else
2041:       @insert_page = @pageset[page]
2042:     end
2043:   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

[Source]

      # File lib/pdf/writer.rb, line 2054
2054:   def insert_position(position = nil)
2055:     return @insert_position unless position
2056:     @insert_position = position
2057:   end

Returns the estimated number of lines remaining given the default or specified font size.

[Source]

      # File lib/pdf/writer.rb, line 2475
2475:   def lines_remaining(font_size = nil)
2476:     font_size ||= @font_size
2477:     remaining = @y - @bottom_margin
2478:     remaining / font_height(font_size).to_f
2479:   end

Define the margins in centimetres.

[Source]

     # File lib/pdf/writer.rb, line 566
566:   def margins_cm(top, left = top, bottom = top, right = left)
567:     margins_pt(cm2pts(top), cm2pts(left), cm2pts(bottom), cm2pts(right))
568:   end

Define the margins in inches.

[Source]

     # File lib/pdf/writer.rb, line 571
571:   def margins_in(top, left = top, bottom = top, right = left)
572:     margins_pt(in2pts(top), in2pts(left), in2pts(bottom), in2pts(right))
573:   end

Define the margins in millimetres.

[Source]

     # File lib/pdf/writer.rb, line 561
561:   def margins_mm(top, left = top, bottom = top, right = left)
562:     margins_pt(mm2pts(top), mm2pts(left), mm2pts(bottom), mm2pts(right))
563:   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

[Source]

     # File lib/pdf/writer.rb, line 582
582:   def margins_pt(top, left = top, bottom = top, right = left)
583:       # Set the margins to new values
584:     @top_margin    = top
585:     @bottom_margin = bottom
586:     @left_margin   = left
587:     @right_margin  = right
588:       # Check to see if this means that the current writing position is
589:       # outside the writable area
590:     if @y > (@page_height - top)
591:         # Move y down
592:       @y = @page_height - top
593:     end
594: 
595:     start_new_page if @y < bottom # Make a new page
596:   end

Convert a measurement in millimetres to points, which are the default PDF userspace units.

[Source]

     # File lib/pdf/writer.rb, line 251
251:   def mm2pts(x)
252:     PDF::Writer.mm2pts(x)
253:   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.

[Source]

     # File lib/pdf/writer.rb, line 550
550:   def move_pointer(dy, make_space = false)
551:     @y -= dy
552:     if @y < @bottom_margin
553:       start_new_page
554:       @y -= dy if make_space
555:     elsif @y > absolute_top_margin
556:       @y = absolute_top_margin
557:     end
558:   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.

[Source]

      # File lib/pdf/writer.rb, line 2113
2113:   def new_page(insert = false, page = nil, pos = :after)
2114:     reset_state_at_page_finish
2115: 
2116:     if insert
2117:         # The id from the PDF::Writer class is the id of the contents of the
2118:         # page, not the page object itself. Query that object to find the
2119:         # parent.
2120:       _new_page = PDF::Writer::Object::Page.new(self, { :rpage => page, :pos => pos })
2121:     else
2122:       _new_page = PDF::Writer::Object::Page.new(self)
2123:     end
2124: 
2125:     reset_state_at_page_start
2126: 
2127:       # If there has been a stroke or fill color set, transfer them.
2128:     fill_color!
2129:     stroke_color!
2130:     stroke_style!
2131: 
2132:       # the call to the page object set @current_contents to the present page,
2133:       # so this can be returned as the page id
2134: #   @current_contents
2135:     _new_page
2136:   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.

[Source]

      # File lib/pdf/writer.rb, line 1858
1858:   def open_at(page, style, *params)
1859:     d = PDF::Writer::Object::Destination.new(self, page, style, *params)
1860:     @catalog.open_here = d
1861:   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.

[Source]

      # File lib/pdf/writer.rb, line 1850
1850:   def open_here(style, *params)
1851:     open_at(@current_page, style, *params)
1852:   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.

[Source]

      # File lib/pdf/writer.rb, line 2739
2739:   def open_new_object
2740:     save_state
2741:     oid = open_object
2742:     close_object
2743:     add_object(oid)
2744:     reopen_object(oid)
2745:     oid
2746:   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.

[Source]

      # File lib/pdf/writer.rb, line 1772
1772:   def open_object
1773:     @stack << { :contents => @current_contents, :page => @current_page }
1774:     @current_contents = PDF::Writer::Object::Contents.new(self)
1775:     @loose_objects << @current_contents
1776:     yield @current_contents if block_given?
1777:     @current_contents
1778:   end

Set the page mode of the catalog. Must be one of the following:

UseNone:Neither document outline nor thumbnail images are visible.
UseOutlines:Document outline visible.
UseThumbs:Thumbnail images visible.
FullScreen:Full-screen mode, with no menu bar, window controls, or any other window visible.
UseOC:Optional content group panel is visible.

[Source]

      # File lib/pdf/writer.rb, line 1898
1898:   def page_mode=(mode)
1899:     @catalog.page_mode = value
1900:   end

Return the PDF stream as a string.

[Source]

     # File lib/pdf/writer.rb, line 703
703:   def render(debug = false)
704:     add_page_numbers
705:     @compression = false if $DEBUG or debug
706:     @arc4.init(@encryption_key) unless @arc4.nil?
707: 
708:     check_all_here
709: 
710:     xref = []
711: 
712:     content = "%PDF-#{@version}\n%âãÏÓ\n"
713:     pos = content.size
714: 
715:     objects.each do |oo|
716:       cont = oo.to_s
717:       content << cont
718:       xref << pos
719:       pos += cont.size
720:     end
721: 
722: #   pos += 1 # Newline character before XREF
723: 
724:     content << "\nxref\n0 #{xref.size + 1}\n0000000000 65535 f \n"
725:     xref.each { |xx| content << "#{'%010d' % [xx]} 00000 n \n" }
726:     content << "\ntrailer\n"
727:     content << "  << /Size #{xref.size + 1}\n"
728:     content << "     /Root 1 0 R\n /Info #{@info.oid} 0 R\n"
729:       # If encryption has been applied to this document, then add the marker
730:       # for this dictionary
731:     if @arc4 and @encryption
732:       content << "/Encrypt #{@encryption.oid} 0 R\n"
733:     end
734: 
735:     if @file_identifier
736:       content << "/ID[<#{@file_identifier}><#{@file_identifier}>]\n"
737:     end
738:     content << "  >>\nstartxref\n#{pos}\n%%EOF\n"
739:     content
740:   end

Opens an existing object for editing.

[Source]

      # File lib/pdf/writer.rb, line 1781
1781:   def reopen_object(id)
1782:     @stack << { :contents => @current_contents, :page => @current_page }
1783:     @current_contents = id
1784:       # if this object is the primary contents for a page, then set the
1785:       # current page to its parent
1786:     @current_page = @current_contents.on_page unless @current_contents.on_page.nil?
1787:     @current_contents
1788:   end

Restore a previously saved state.

[Source]

      # File lib/pdf/writer.rb, line 1750
1750:   def restore_state
1751:     unless @state_stack.empty?
1752:       state = @state_stack.pop
1753:       @current_fill_color         = state.fill_color
1754:       @current_stroke_color       = state.stroke_color
1755:       @current_text_render_style  = state.text_render_style
1756:       @current_stroke_style       = state.stroke_style
1757:       stroke_style!
1758:     end
1759:     add_content("\nQ")
1760:   end

Save the PDF as a file to disk.

[Source]

      # File lib/pdf/writer.rb, line 2749
2749:   def save_as(name)
2750:     File.open(name, "wb") { |f| f.write self.render }
2751:   end

Saves the state.

[Source]

      # File lib/pdf/writer.rb, line 1724
1724:   def save_state
1725:     PDF::Writer::State.new do |state|
1726:       state.fill_color        = @current_fill_color
1727:       state.stroke_color      = @current_stroke_color
1728:       state.text_render_style = @current_text_render_style
1729:       state.stroke_style      = @current_stroke_style
1730:       @state_stack.push state
1731:     end
1732:     add_content("\nq")
1733:   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 }.

[Source]

      # File lib/pdf/writer.rb, line 1004
1004:   def select_font(font, encoding = nil)
1005:     load_font(font, encoding) unless @fonts[font]
1006: 
1007:     @current_base_font = font
1008:     current_font!
1009:     @current_base_font
1010:   end

The number of PDF objects in the document

[Source]

     # File lib/pdf/writer.rb, line 137
137:   def size
138:     @objects.size
139:   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.

[Source]

      # File lib/pdf/writer.rb, line 1939
1939:   def start_columns(size = 2, gutter = 10)
1940:       # Start from the current y-position; make the set number of columns.
1941:     return false if @columns_on
1942: 
1943:     @columns = {
1944:       :current => 1,
1945:       :bot_y   => @y
1946:     }
1947:     @columns_on = true
1948:       # store the current margins
1949:     @columns[:left]   = @left_margin
1950:     @columns[:right]  = @right_margin
1951:     @columns[:top]    = @top_margin
1952:     @columns[:bottom] = @bottom_margin
1953:       # Reset the margins to suit the new columns. Safe enough to assume the
1954:       # first column here, but start from the current y-position.
1955:     @top_margin = @page_height - @y
1956:     @columns[:size]   = size   || 2
1957:     @columns[:gutter] = gutter || 10
1958:     w = absolute_right_margin - absolute_left_margin
1959:     @columns[:width] = (w - ((size - 1) * gutter)) / size.to_f
1960:     @right_margin = @page_width - (@left_margin + @columns[:width])
1961:   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.

[Source]

      # File lib/pdf/writer.rb, line 2063
2063:   def start_new_page(force = false)
2064:     page_required = true
2065: 
2066:     if @columns_on
2067:         # Check if this is just going to a new column. Increment the column
2068:         # number.
2069:       @columns[:current] += 1
2070: 
2071:       if @columns[:current] <= @columns[:size] and not force
2072:         page_required = false
2073:         @columns[:bot_y] = @y if @y < @columns[:bot_y]
2074:       else
2075:         @columns[:current] = 1
2076:         @top_margin = @columns[:top]
2077:         @columns[:bot_y] = absolute_top_margin
2078:       end
2079: 
2080:       w = @columns[:width]
2081:       g = @columns[:gutter]
2082:       n = @columns[:current] - 1
2083:       @left_margin = @columns[:left] + n * (g + w)
2084:       @right_margin = @page_width - (@left_margin + w)
2085:     end
2086: 
2087:     if page_required or force
2088:         # make a new page, setting the writing point back to the top.
2089:       @y = absolute_top_margin
2090:         # make the new page with a call to the basic class
2091:       if @insert_mode
2092:         id = new_page(true, @insert_page, @insert_position)
2093:         @pageset << id
2094:           # Manipulate the insert options so that inserted pages follow each
2095:           # other
2096:         @insert_page = id
2097:         @insert_position = :after
2098:       else
2099:         @pageset << new_page
2100:       end
2101: 
2102:     else
2103:       @y = absolute_top_margin
2104:     end
2105:     @pageset
2106:   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.

[Source]

      # File lib/pdf/writer.rb, line 2158
2158:   def start_page_numbering(x, y, size, pos = nil, pattern = nil, starting = nil)   
2159:     pos     ||= :left
2160:     pattern ||= "<PAGENUM> of <TOTALPAGENUM>"
2161:     starting  ||= 1
2162: 
2163:     @page_numbering ||= []
2164:     @page_numbering << (o = {})
2165: 
2166:     page    = @pageset.size - 1
2167:     o[page] = {
2168:       :x        => x,
2169:       :y        => y,
2170:       :pos      => pos,
2171:       :pattern  => pattern,
2172:       :starting => starting,
2173:       :size     => size,
2174:       :start    => true
2175:     }
2176:     @page_numbering.index(o)
2177:   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.

[Source]

      # File lib/pdf/writer.rb, line 1975
1975:   def stop_columns
1976:     return false unless @columns_on
1977:     @columns_on = false
1978: 
1979:     @columns[:bot_y] = @y if @y < @columns[:bot_y]
1980: 
1981:     if (@columns[:bot_y] > @bottom_margin) or @column_number == 1
1982:       @y = @columns[:bot_y]
1983:     else
1984:       start_new_page
1985:     end
1986:     restore_margins_after_columns
1987:     @columns = {}
1988:     true
1989:   end

Stop an object from appearing on pages from this point on.

[Source]

      # File lib/pdf/writer.rb, line 1800
1800:   def stop_object(id)
1801:     obj = @loose_objects.detect { |ii| ii.oid == id.oid }
1802:     @add_loose_objects[obj] = nil
1803:   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.

[Source]

      # File lib/pdf/writer.rb, line 2216
2216:   def stop_page_numbering(stop_total = false, stop_at = :current, scheme = 0)
2217:     return false unless @page_numbering
2218: 
2219:     page = @pageset.size - 1
2220: 
2221:     @page_numbering[scheme][page] ||= {}
2222:     o = @page_numbering[scheme][page]
2223: 
2224:     case [ stop_total, stop_at == :current ]
2225:     when [ true, true ]
2226:       o[:stop] = :stop_total
2227:     when [ true, false ]
2228:       o[:stop] = :stop_total_next
2229:     when [ false, true ]
2230:       o[:stop] = :stop_next
2231:       else
2232:       o[:stop] = :stop
2233:     end
2234:   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.

[Source]

      # File lib/pdf/writer.rb, line 2363
2363:   def text(text, options = {})
2364:       # Apply the filtering which will make underlining (and other items)
2365:       # function.
2366:     text = preprocess_text(text)
2367: 
2368:     options ||= {}
2369: 
2370:     new_page_required = false
2371:     __y = @y
2372: 
2373:     if options[:absolute_left]
2374:       left = options[:absolute_left]
2375:     else
2376:       left = @left_margin
2377:       left += options[:left] if options[:left]
2378:     end
2379: 
2380:     if options[:absolute_right]
2381:       right = options[:absolute_right]
2382:     else
2383:       right = absolute_right_margin
2384:       right -= options[:right] if options[:right]
2385:     end
2386: 
2387:     size = options[:font_size] || 0
2388:     if size <= 0
2389:       size = @font_size
2390:     else
2391:       @font_size = size
2392:     end
2393: 
2394:     just = options[:justification] || :left
2395: 
2396:     if options[:leading] # leading instead of spacing
2397:       height = options[:leading]
2398:     elsif options[:spacing]
2399:       height = options[:spacing] * font_height(size)
2400:     else
2401:       height = font_height(size)
2402:     end
2403: 
2404:     text.each do |line|
2405:       start = true
2406:       loop do # while not line.empty? or start
2407:         break if (line.nil? or line.empty?) and not start
2408: 
2409:         start = false
2410: 
2411:         @y -= height
2412: 
2413:         if @y < @bottom_margin
2414:           if options[:test]
2415:             new_page_required = true
2416:           else
2417:               # and then re-calc the left and right, in case they have
2418:               # changed due to columns
2419:             start_new_page
2420:             @y -= height
2421: 
2422:             if options[:absolute_left]
2423:               left = options[:absolute_left]
2424:             else
2425:               left = @left_margin
2426:               left += options[:left] if options[:left]
2427:             end
2428: 
2429:             if options[:absolute_right]
2430:               right = options[:absolute_right]
2431:             else
2432:               right = absolute_right_margin
2433:               right -= options[:right] if options[:right]
2434:             end
2435:           end
2436:         end
2437: 
2438:         line = add_text_wrap(left, @y, right - left, line, size, just, 0, options[:test])
2439:       end
2440:     end
2441: 
2442:     if options[:test]
2443:       @y = __y
2444:       new_page_required
2445:     else
2446:       @y
2447:     end
2448:   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.

[Source]

      # File lib/pdf/writer.rb, line 1518
1518:   def text_line_width(text, size = nil)
1519:     if text.kind_of?(Numeric) and size.kind_of?(String)
1520:       text, size = size, text
1521:       warn PDF::Writer::Lang[:text_width_parameters_reversed] % caller[0]
1522:     end
1523: 
1524:     if size.nil? or size <= 0
1525:       size = @font_size
1526:     end
1527: 
1528:       # This function should not change any of the settings, though it will
1529:       # need to track any tag which change during calculation, so copy them
1530:       # at the start and put them back at the end.
1531:     t_CTS = @current_text_state.dup
1532: 
1533:     select_font("Helvetica") if @fonts.empty?
1534:       # converts a number or a float to a string so it can get the width
1535:     tt = text.to_s
1536:       # hmm, this is where it all starts to get tricky - use the font
1537:       # information to calculate the width of each character, add them up
1538:       # and convert to user units
1539:     width = 0
1540:     font = @current_font
1541: 
1542:     pos = -1
1543:     loop do
1544:       pos += 1
1545:       break if pos == tt.size
1546:       font_change = true
1547:       tag_size, text, font_change = quick_text_tags(text, pos, font_change)
1548:       if tag_size != 0
1549:         if font_change
1550:           current_font!
1551:           font = @current_font
1552:         end
1553:         pos += tag_size - 1
1554:       else
1555:         if "&lt;" == tt[pos, 4]
1556:           width += char_width(font, '<')
1557:           pos += 3
1558:         elsif "&gt;" == tt[pos, 4]
1559:           width += char_width(font, '>')
1560:           pos += 3
1561:         elsif "&amp;" == tt[pos, 5]
1562:           width += char_width(font, '&')
1563:           pos += 4
1564:         else
1565:           width += char_width(font, tt[pos, 1])
1566:         end
1567:       end
1568:     end
1569: 
1570:     @current_text_state = t_CTS.dup
1571:     current_font!
1572: 
1573:     (width * size / 1000.0)
1574:   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.

[Source]

      # File lib/pdf/writer.rb, line 1583
1583:   def text_width(text, size = nil)
1584:     if text.kind_of?(Numeric) and size.kind_of?(String)
1585:       text, size = size, text
1586:       warn PDF::Writer::Lang[:text_width_parameters_reversed] % caller[0]
1587:     end
1588: 
1589:     if size.nil? or size <= 0
1590:       size = @font_size
1591:     end
1592: 
1593:     max   = 0
1594: 
1595:     text.to_s.each do |line|
1596:       width = text_line_width(line, size)
1597:       max = width if width > max
1598:     end
1599:     max
1600:   end
to_s(debug = false)

Alias for render

Sets the trim box area.

[Source]

     # File lib/pdf/writer.rb, line 652
652:   def trim_box(x0, y0, x1, y1)
653:     @pages.trim_box = [ x0, y0, x1, y1 ]
654:   end

set the viewer preferences of the document, it is up to the browser to obey these.

[Source]

     # File lib/pdf/writer.rb, line 663
663:   def viewer_preferences(label, value = 0)
664:     @catalog.viewer_preferences ||= PDF::Writer::Object::ViewerPreferences.new(self)
665: 
666:       # This will only work if the label is one of the valid ones.
667:     if label.kind_of?(Hash)
668:       label.each { |kk, vv| @catalog.viewer_preferences.__send__("#{kk.downcase}=".intern, vv) }
669:     else
670:       @catalog.viewer_preferences.__send__("#{label.downcase}=".intern, value)
671:     end
672:   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.

[Source]

      # File lib/pdf/writer.rb, line 2186
2186:   def which_page_number(page_num, scheme = 0)
2187:     return nil unless @page_numbering
2188: 
2189:     num   = nil
2190:     start = start_num = 1
2191: 
2192:     @page_numbering[scheme].each do |kk, vv|
2193:       if kk <= page_num
2194:         if vv.kind_of?(Hash)
2195:           unless vv[:starting].nil?
2196:             start = vv[:starting]
2197:             start_num = kk
2198:             num = page_num - start_num + start
2199:           end
2200:         else
2201:           num = nil
2202:         end
2203:       end
2204:     end
2205:     num
2206:   end

Private Instance methods

[Source]

      # File lib/pdf/writer.rb, line 2243
2243:   def add_page_numbers
2244:       # This will go through the @page_numbering array and add the page
2245:       # numbers are required.
2246:     if @page_numbering                           
2247:       page_count  = @pageset.size
2248:       pn_tmp      = @page_numbering.dup
2249: 
2250:         # Go through each of the page numbering schemes.
2251:       pn_tmp.each do |scheme|
2252:           # First, find the total pages for this schemes.
2253:         page = page_number_search(:stop_total, scheme)
2254: 
2255:         if page                                       
2256:           total_pages = page
2257:         else
2258:           page = page_number_search(:stop_total_next, scheme)        
2259:           if page
2260:             total_pages = page
2261:           else
2262:             total_pages = page_count
2263:           end
2264:         end
2265: 
2266:         status  = nil
2267:         delta   = pattern = pos = x = y = size = nil 
2268:         pattern = pos = x = y = size = nil
2269: 
2270:         @pageset.each_with_index do |page, index|
2271:           next if status.nil? and scheme[index].nil?
2272: 
2273:           info = scheme[index]
2274:           if info
2275:             if info[:start]
2276:               status = true
2277:               if info[:starting] 
2278:                 delta = info[:starting] - index 
2279:               else 
2280:                 delta = index 
2281:               end  
2282: 
2283:               pattern = info[:pattern]
2284:               pos     = info[:pos]
2285:               x       = info[:x]
2286:               y       = info[:y]
2287:               size    = info[:size]
2288: 
2289:               # Check for the special case of page numbering starting and
2290:               # stopping on the same page.
2291:               status = :stop_next if info[:stop]
2292:             elsif [:stop, :stop_total].include?(info[:stop])
2293:               status = :stop_now
2294:             elsif status == true and [:stop_next, :stop_total_next].include?(info[:stop])
2295:               status = :stop_next
2296:             end
2297:           end
2298: 
2299:           if status
2300:               # Add the page numbering to this page
2301:             num   = index + delta.to_i 
2302:             total = total_pages + num - index
2303:             patt  = pattern.gsub(/<PAGENUM>/, num.to_s).gsub(/<TOTALPAGENUM>/, total.to_s)
2304:             reopen_object(page.contents.first)
2305: 
2306:             case pos
2307:             when :left    # Write the page number from x.
2308:               w = 0
2309:             when :right   # Write the page number to x.
2310:               w = text_width(patt, size)
2311:             when :center  # Write the page number around x.
2312:               w = text_width(patt, size) / 2.0
2313:             end
2314:             add_text(x - w, y, patt, size)
2315:             close_object
2316:             status = nil if [ :stop_now, :stop_next ].include?(status)
2317:           end
2318:         end
2319:       end
2320:     end
2321:   end

Partially calculate the values necessary to sort out the justification of text.

[Source]

      # File lib/pdf/writer.rb, line 1604
1604:   def adjust_wrapped_text(text, actual, width, x, just)
1605:     adjust  = 0
1606: 
1607:     case just
1608:     when :left
1609:       nil
1610:     when :right
1611:       x += (width - actual)
1612:     when :center
1613:       x += (width - actual) / 2.0
1614:     when :full
1615:       spaces = text.count(" ")
1616:       adjust = (width - actual) / spaces.to_f if spaces > 0
1617:     end
1618: 
1619:     [x, adjust]
1620:   end

[Source]

      # File lib/pdf/writer.rb, line 1497
1497:   def char_width(font, char)
1498:     char = char[0] unless @fonts[font].c[char]
1499: 
1500:     if @fonts[font].differences and @fonts[font].c[char].nil?
1501:       name = @fonts[font].differences[char] || 'M'
1502:       width = @fonts[font].c[name]['WX'] if @fonts[font].c[name]['WX']
1503:     elsif @fonts[font].c[char]
1504:       width = @fonts[font].c[char]['WX']
1505:     else
1506:       width = @fonts[font].c['M']['WX']
1507:     end
1508:     width
1509:   end

[Source]

     # File lib/pdf/writer.rb, line 754
754:   def find_font(fontname)
755:     name = File.basename(fontname, ".afm")
756:     @objects.detect do |oo|
757:       oo.kind_of?(PDF::Writer::Object::Font) and /#{oo.basefont}$/ =~ name
758:     end
759:   end

[Source]

     # File lib/pdf/writer.rb, line 762
762:   def font_file(fontfile)
763:     path = "#{fontfile}.pfb"
764:     return path if File.exists?(path)
765:     path = "#{fontfile}.ttf"
766:     return path if File.exists?(path)
767:     nil
768:   end

Generate a new font ID.

[Source]

     # File lib/pdf/writer.rb, line 148
148:   def generate_font_id
149:     @mutex.synchronize { @current_font_id += 1 }
150:   end

Generate an ID for a new PDF object.

[Source]

     # File lib/pdf/writer.rb, line 142
142:   def generate_id
143:     @mutex.synchronize { @current_id += 1 }
144:   end

Initialize the font families for the default fonts.

[Source]

     # File lib/pdf/writer.rb, line 623
623:   def init_font_families
624:       # Set the known family groups. These font families will be used to
625:       # enable bold and italic markers to be included within text
626:       # streams. HTML forms will be used... <b></b> <i></i>
627:     @font_families["Helvetica"] =
628:     {
629:       "b"   => 'Helvetica-Bold',
630:       "i"   => 'Helvetica-Oblique',
631:       "bi"  => 'Helvetica-BoldOblique',
632:       "ib"  => 'Helvetica-BoldOblique'
633:     }
634:     @font_families['Courier'] =
635:     {
636:       "b"   => 'Courier-Bold',
637:       "i"   => 'Courier-Oblique',
638:       "bi"  => 'Courier-BoldOblique',
639:       "ib"  => 'Courier-BoldOblique'
640:     }
641:     @font_families['Times-Roman'] =
642:     {
643:       "b"   => 'Times-Bold',
644:       "i"   => 'Times-Italic',
645:       "bi"  => 'Times-BoldItalic',
646:       "ib"  => 'Times-BoldItalic'
647:     }
648:   end

[Source]

     # File lib/pdf/writer.rb, line 771
771:   def load_font(font, encoding = nil)
772:     metrics = load_font_metrics(font)
773: 
774:     name  = File.basename(font).gsub(/\.afm$/o, "")
775: 
776:     encoding_diff = nil
777:     case encoding
778:     when Hash
779:       encoding_name = encoding[:encoding]
780:       encoding_diff = encoding[:differences]
781:       encoding      = PDF::Writer::Object::FontEncoding.new(self, encoding_name, encoding_diff)
782:     when NilClass
783:       encoding_name = encoding = 'WinAnsiEncoding'
784:     else
785:       encoding_name = encoding
786:     end
787: 
788:     wfo = PDF::Writer::Object::Font.new(self, name, encoding)
789: 
790:       # We have an Adobe Font Metrics (.afm) file. We need to find the
791:       # associated Type1 (.pfb) or TrueType (.ttf) files (we do not yet
792:       # support OpenType fonts); we need to load it into a
793:       # PDF::Writer::Object and put the references into the metrics object.
794:     base = metrics.path.sub(/\.afm$/o, "")
795:     fontfile = font_file(base)
796:     unless fontfile
797:       base = File.basename(base)
798:       FONT_PATH.each do |path|
799:         fontfile = font_file(File.join(path, base))
800:         break if fontfile
801:       end
802:     end
803: 
804:     if font =~ /afm/o and fontfile
805:         # Find the array of font widths, and put that into an object.
806:       first_char  = -1
807:       last_char   = 0
808: 
809:       widths = {}
810:       metrics.c.each_value do |details|
811:         num = details["C"]
812: 
813:         if num >= 0
814:           # warn "Multiple definitions of #{num}" if widths.has_key?(num)
815:           widths[num] = details['WX']
816:           first_char = num if num < first_char or first_char < 0
817:           last_char = num if num > last_char
818:         end
819:       end
820: 
821:       # Adjust the widths for the differences array.
822:       if encoding_diff
823:         encoding_diff.each do |cnum, cname|
824:           (cnum - last_char).times { widths << 0 } if cnum > last_char
825:           last_char = cnum
826:           widths[cnum - firstchar] = fonts.c[cname]['WX'] if metrics.c[cname]
827:         end
828:       end
829: 
830:       widthid = PDF::Writer::Object::Contents.new(self, :raw)
831:       widthid << "["
832:       (first_char .. last_char).each do |ii|
833:         if widths.has_key?(ii)
834:           widthid << " #{widths[ii].to_i}"
835:         else
836:           widthid << " 0"
837:         end
838:       end
839:       widthid << "]"
840: 
841:         # Load the pfb file, and put that into an object too. Note that PDF
842:         # supports only binary format Type1 font files and TrueType font
843:         # files. There is a simple utility to convert Type1 from pfa to pfb.
844:       data = File.open(fbfile, "rb") { |ff| ff.read }
845: 
846:         # Check to see if the font licence allows embedding.
847:       if fbtype =~ /\.ttf$/o
848:         offset  = 4
849:         tables  = data[offset, 2].unpack('n')[0]
850:         offset += 8
851: 
852:         found   = false
853:         tables.times do
854:           if data[offset, 4] == 'OS/2'
855:             found = true
856:             break
857:           end
858:           offset += 4 + 12
859:         end
860: 
861:         if found
862:           offset += 4
863:           newoff  = data[offset, 4].unpack('N')[0]
864:           offset  = newoff + 8
865:           licence = data[offset, 2].unpack('n')[0]
866: 
867:           rl  = ((licence & 0x02) != 0)
868:           pp  = ((licence & 0x04) != 0)
869:           ee  = ((licence & 0x08) != 0)
870: 
871:           if rl and pp and ee
872:             warn PDF::Writer::Lang[:ttf_licence_no_embedding] % name
873:           end
874:         end
875:       end
876: 
877:         # Create the font descriptor.
878:       fdsc = PDF::Writer::Object::FontDescriptor.new(self)
879:         # Raw contents causes problems with Acrobat Reader.
880:       pfbc = PDF::Writer::Object::Contents.new(self)
881: 
882:         # Determine flags (more than a little flakey, hopefully will not
883:         # matter much).
884:       flags = 0
885:       if encoding == "none"
886:         flags += 2 ** 2
887:       else
888:         flags += 2 ** 6 if metrics.italicangle.nonzero?
889:         flags += 2 ** 0 if metrics.isfixedpitch == "true"
890:         flags += 2 ** 5 # Assume a non-symbolic font
891:       end
892: 
893:         # 1: FixedPitch:  All glyphs have the same width (as opposed to
894:         #                 proportional or variable-pitch fonts, which have
895:         #                 different widths).
896:         # 2: Serif:       Glyphs have serifs, which are short strokes drawn
897:         #                 at an angle on the top and bottom of glyph stems.
898:         #                 (Sans serif fonts do not have serifs.)
899:         # 3: Symbolic     Font contains glyphs outside the Adobe standard
900:         #                 Latin character set. This flag and the Nonsymbolic
901:         #                 flag cannot both be set or both be clear (see
902:         #                 below).
903:         # 4: Script:      Glyphs resemble cursive handwriting.
904:         # 6: Nonsymbolic: Font uses the Adobe standard Latin character set
905:         #                 or a subset of it (see below).
906:         # 7: Italic:      Glyphs have dominant vertical strokes that are
907:         #                 slanted.
908:         # 17: AllCap:     Font contains no lowercase letters; typically used
909:         #                 for display purposes, such as for titles or
910:         #                 headlines.
911:         # 18: SmallCap:   Font contains both uppercase and lowercase
912:         #                 letters. The uppercase letters are similar to
913:         #                 those in the regular version of the same typeface
914:         #                 family. The glyphs for the lowercase letters have
915:         #                 the same shapes as the corresponding uppercase
916:         #                 letters, but they are sized and their proportions
917:         #                 adjusted so that they have the same size and
918:         #                 stroke weight as lowercase glyphs in the same
919:         #                 typeface family.
920:         # 19: ForceBold:  See below.
921: 
922:       list = {
923:         'Ascent'      => 'Ascender',
924:         'CapHeight'   => 'CapHeight',
925:         'Descent'     => 'Descender',
926:         'FontBBox'    => 'FontBBox',
927:         'ItalicAngle' => 'ItalicAngle'
928:       }
929:       fdopt = {
930:         'Flags'     => flags,
931:         'FontName'  => metrics.fontname,
932:         'StemV'     => 100 # Don't know what the value for this should be!
933:       }
934: 
935:       list.each do |kk, vv|
936:         zz = metrics.__send__(vv.downcase.intern)
937:         fdopt[kk] = zz if zz
938:       end
939: 
940:         # Determine the cruicial lengths within this file
941:       if fbtype =~ /\.pfb$/o
942:         fdopt['FontFile'] = pfbc.oid
943:         i1 = data.index('eexec') + 6
944:         i2 = data.index('00000000')  - i1
945:         i3 = data.size - i2 - i1
946:         pfbc.add('Length1' => i1, 'Length2' => i2, 'Length3' => i3)
947:       elsif fbtype =~ /\.ttf$/o
948:         fdopt['FontFile2'] = pfbc.oid
949:         pfbc.add('Length1' => data.size)
950:       end
951: 
952:       fdsc.options = fdopt
953:         # Embed the font program
954:       pfbc << data
955: 
956:       # Tell the font object about all this new stuff
957:       tmp = {
958:         'BaseFont'        => metrics.fontname,
959:         'Widths'          => widthid.oid,
960:         'FirstChar'       => first_char,
961:         'LastChar'        => last_char,
962:         'FontDescriptor'  => fdsc.oid
963:       }
964:       tmp['SubType'] = 'TrueType' if fbtype == "ttf"
965: 
966:       tmp.each { |kk, vv| wfo.__send__("#{kk.downcase}=".intern, vv) }
967:     end
968: 
969:       # Also set the differences here. Note that this means that these will
970:       # take effect only the first time that a font is selected, else they
971:       # are ignored.
972:     metrics.differences = encoding_diff unless encoding_diff.nil?
973:     metrics.encoding = encoding_name
974:     metrics
975:   end

Loads the font metrics. This is now thread-safe.

[Source]

     # File lib/pdf/writer.rb, line 744
744:   def load_font_metrics(font)
745:     metrics = PDF::Writer::FontMetrics.open(font)
746:     @mutex.synchronize do
747:       @fonts[font] = metrics
748:       @fonts[font].font_num = @fonts.size
749:     end
750:     metrics
751:   end

[Source]

      # File lib/pdf/writer.rb, line 2236
2236:   def page_number_search(condition, scheme)
2237:     res = nil
2238:     scheme.each { |page, value| res = page if value[:stop] == condition }
2239:     res
2240:   end

[Source]

      # File lib/pdf/writer.rb, line 1375
1375:   def parse_tag_params(params)
1376:     params ||= ""
1377:     ph = {}
1378:     params.scan(TAG_PARAM_RE) do |param|
1379:       ph[param[0]] = param[1] || param[2] || param[3]
1380:     end
1381:     ph
1382:   end

[Source]

      # File lib/pdf/writer.rb, line 2324
2324:   def preprocess_text(text)
2325:     text
2326:   end

Wrapper function for text_tags

[Source]

      # File lib/pdf/writer.rb, line 1095
1095:   def quick_text_tags(text, ii, font_change)
1096:     ret = text_tags(text, ii, font_change)
1097:     [ret[0], ret[1], ret[2]]
1098:   end

Restore the state at the end of a page.

[Source]

      # File lib/pdf/writer.rb, line 1763
1763:   def reset_state_at_page_finish
1764:     add_content("\nQ" * @state_stack.size)
1765:   end

This will be called at a new page to return the state to what it was on the end of the previous page, before the stack was closed down. This is to get around not being able to have open ‘q’ across pages.

[Source]

      # File lib/pdf/writer.rb, line 1738
1738:   def reset_state_at_page_start
1739:     @state_stack.each do |state|
1740:       fill_color!         state.fill_color
1741:       stroke_color!       state.stroke_color
1742:       text_render_style!  state.text_render_style
1743:       stroke_style!       state.stroke_style
1744:       add_content("\nq")
1745:     end
1746:   end

[Source]

      # File lib/pdf/writer.rb, line 1963
1963:   def restore_margins_after_columns
1964:     @left_margin   = @columns[:left]
1965:     @right_margin  = @columns[:right]
1966:     @top_margin    = @columns[:top]
1967:     @bottom_margin = @columns[:bottom]
1968:   end

Given a start position and information about how text is to be laid out, calculate where on the page the text will end.

[Source]

      # File lib/pdf/writer.rb, line 1086
1086:   def text_end_position(x, y, angle, size, wa, text)
1087:     width = text_width(text, size)
1088:     width += wa * (text.count(" "))
1089:     rad = PDF::Math.deg2rad(angle)
1090:     [Math.cos(rad) * width + x, ((-Math.sin(rad)) * width + y)]
1091:   end

Checks if text contains a control tag at pos. Control tags are XML-like tags that contain tag information.

Supported Tag Formats

&lt;b>:Adds b to the end of the current text state. If this is the closing tag, &lt;/b>, b is removed from the end of the current text state.
&lt;i>:Adds i to the end of the current text state. If this is the closing tag, &lt;/i, i is removed from the end of the current text state.
&lt;r:TAG[ PARAMS]/>:Calls a stand-alone replace callback method of the form tag_TAG_replace. PARAMS must be separated from the TAG name by a single space. The PARAMS, if present, are passed to the replace callback unmodified, whose responsibility it is to interpret the parameters. The replace callback is expected to return text that will be used in the place of the tag. text_tags is called again immediately so that if the replacement text has tags, they will be dealt with properly.
&lt;C:TAG[ PARAMS]/>:Calls a stand-alone drawing callback method. The method will be provided an information hash (see below for the data provided). It is expected to use this information to perform whatever drawing tasks are needed to perform its task.
&lt;c:TAG[ PARAMS]>:Calls a paired drawing callback method. The method will be provided an information hash (see below for the data provided). It is expected to use this information to perform whatever drawing tasks are needed to perform its task. It must have a corresponding &lt;/c:TAG> closing tag. Paired callback behaviours will be preserved over page breaks and line changes.

Drawing callback tags will be provided an information hash that tells the callback method where it must perform its drawing tasks.

Drawing Callback Parameters

:x:The current X position of the text.
:y:The current y position of the text.
:angle:The current text drawing angle.
:params:Any parameters that may be important to the callback. This value is only guaranteed to have meaning when a stand-alone callback is made or the opening tag is processed.
:status::start, :end, :start_line, :end_line
:cbid:The identifier of this callback. This may be used as a key into a different variable where state may be kept.
:callback:The name of the callback function. Only set for stand-alone or opening callback tags.
:height:The font height.
:descender:The font descender size.

:status Values and Meanings

:start:The callback has been started. This applies either when the callback is a stand-alone callback (&lt;C:TAG/>) or the opening tag of a paired tag (&lt;c:TAG>).
:end:The callback has been manually terminated with a closing tag (&lt;/c:TAG>).
:start_line:Called when a new line is to be drawn. This allows the callback to perform any updates necessary to permit paired callbacks to cross line boundaries. This will usually involve updating x, y positions.
:end_line:Called when the end of a line is reached. This permits the callback to perform any drawing necessary to permit paired callbacks to cross line boundaries.

Drawing callback methods may return a hash of the :x and :y position that the drawing pointer should take after the callback is complete.

Known Callback Tags

&lt;c:alink URI>:makes an external link around text between the opening and closing tags of this callback. The URI may be any URL, including http://, ftp://, and mailto:, as long as there is a URL handler registered. URI is of the form uri="URI".
&lt;c:ilink DEST>:makes an internal link within the document. The DEST must refer to a known named destination within the document. DEST is of the form dest="DEST".
&lt;c:uline>:underlines the specified text.
&lt;C:bullet>:Draws a solid bullet at the tag position.
&lt;C:disc>:Draws a disc bullet at the tag position.

[Source]

      # File lib/pdf/writer.rb, line 1209
1209:   def text_tags(text, pos, font_change, final = false, x = 0, y = 0, size = 0, angle = 0, word_space_adjust = 0)
1210:     tag_size = 0
1211: 
1212:     tag_match = %r!^<(/)?([^>]+)>!.match(text[pos..-1])
1213: 
1214:     if tag_match
1215:       closed, tag_name = tag_match.captures
1216:       cts = @current_text_state # Alias for shorter lines.
1217:       tag_size = tag_name.size + 2 + (closed ? 1 : 0)
1218: 
1219:       case tag_name
1220:       when %r{^(?:b|strong)$}o
1221:         if closed
1222:           cts.slice!(-1, 1) if ?b == cts[-1]
1223:         else
1224:           cts << ?b
1225:         end
1226:       when %r{^(?:i|em)$}o
1227:         if closed
1228:           cts.slice!(-1, 1) if ?i == cts[-1]
1229:         else
1230:           cts << ?i
1231:         end
1232:       when %r{^r:}o
1233:         _match = MATCH_TAG_REPLACE_RE.match(tag_name)
1234:         if _match.nil?
1235:           warn PDF::Writer::Lang[:callback_warning] % [ 'r:', tag_name ]
1236:           tag_size = 0
1237:         else
1238:           func    = _match.captures[0]
1239:           params  = parse_tag_params(_match.captures[1] || "")
1240:           tag     = TAGS[:replace][func]
1241: 
1242:           if tag
1243:             text[pos, tag_size] = tag[self, params]
1244:             tag_size, text, font_change, x, y = text_tags(text, pos,
1245:                                                           font_change,
1246:                                                           final, x, y, size,
1247:                                                           angle,
1248:                                                           word_space_adjust)
1249:           else
1250:             warn PDF::Writer::Lang[:callback_warning] % [ 'r:', func ]
1251:             tag_size = 0
1252:           end
1253:         end
1254:       when %r{^C:}o
1255:         _match = MATCH_TAG_DRAW_ONE_RE.match(tag_name)
1256:         if _match.nil?
1257:           warn PDF::Writer::Lang[:callback_warning] % [ 'C:', tag_name ]
1258:           tag_size = 0
1259:         else
1260:           func    = _match.captures[0]
1261:           params  = parse_tag_params(_match.captures[1] || "")
1262:           tag     = TAGS[:single][func]
1263: 
1264:           if tag
1265:             font_change = false
1266: 
1267:             if final
1268:               # Only call the function if this is the "final" call. Assess
1269:               # the text position. Calculate the text width to this point.
1270:               x, y = text_end_position(x, y, angle, size, word_space_adjust,
1271:                                        text[0, pos])
1272:               info = {
1273:                 :x          => x,
1274:                 :y          => y,
1275:                 :angle      => angle,
1276:                 :params     => params,
1277:                 :status     => :start,
1278:                 :cbid       => @callbacks.size + 1,
1279:                 :callback   => func,
1280:                 :height     => font_height(size),
1281:                 :descender  => font_descender(size)
1282:               }
1283: 
1284:               ret = tag[self, info]
1285:               if ret.kind_of?(Hash)
1286:                 ret.each do |rk, rv|
1287:                   x           = rv if rk == :x
1288:                   y           = rv if rk == :y
1289:                   font_change = rv if rk == :font_change
1290:                 end
1291:               end
1292:             end
1293:           else
1294:             warn PDF::Writer::Lang[:callback_Warning] % [ 'C:', func ]
1295:             tag_size = 0
1296:           end
1297:         end
1298:       when %r{^c:}o
1299:         _match = MATCH_TAG_DRAW_PAIR_RE.match(tag_name)
1300: 
1301:         if _match.nil?
1302:           warn PDF::Writer::Lang[:callback_warning] % [ 'c:', tag_name ]
1303:           tag_size = 0
1304:         else
1305:           func    = _match.captures[0]
1306:           params  = parse_tag_params(_match.captures[1] || "")
1307:           tag     = TAGS[:pair][func]
1308: 
1309:           if tag
1310:             font_change = false
1311: 
1312:             if final
1313:                 # Only call the function if this is the "final" call. Assess
1314:                 # the text position. Calculate the text width to this point.
1315:               x, y = text_end_position(x, y, angle, size, word_space_adjust,
1316:                                        text[0, pos])
1317:               info = {
1318:                 :x          => x,
1319:                 :y          => y,
1320:                 :angle      => angle,
1321:                 :params     => params,
1322:               }
1323: 
1324:               if closed
1325:                 info[:status] = :end
1326:                 info[:cbid]   = @callbacks.size
1327: 
1328:                 ret = tag[self, info]
1329: 
1330:                 if ret.kind_of?(Hash)
1331:                   ret.each do |rk, rv|
1332:                     x           = rv if rk == :x
1333:                     y           = rv if rk == :y
1334:                     font_change = rv if rk == :font_change
1335:                   end
1336:                 end
1337: 
1338:                 @callbacks.pop
1339:               else
1340:                 info[:status]     = :start
1341:                 info[:cbid]       = @callbacks.size + 1
1342:                 info[:tag]        = tag
1343:                 info[:callback]   = func
1344:                 info[:height]     = font_height(size)
1345:                 info[:descender]  = font_descender(size)
1346: 
1347:                 @callbacks << info
1348: 
1349:                 ret = tag[self, info]
1350: 
1351:                 if ret.kind_of?(Hash)
1352:                   ret.each do |rk, rv|
1353:                     x           = rv if rk == :x
1354:                     y           = rv if rk == :y
1355:                     font_change = rv if rk == :font_change
1356:                   end
1357:                 end
1358:               end
1359:             end
1360:           else
1361:             warn PDF::Writer::Lang[:callback_warning] % [ 'c:', func ]
1362:             tag_size = 0
1363:           end
1364:         end
1365:       else
1366:         tag_size = 0
1367:       end
1368:     end
1369:     [ tag_size, text, font_change, x, y ]
1370:   end

[Validate]