Class PDF::Writer
In: lib/pdf/writer/state.rb
lib/pdf/writer.rb
Parent: Object
TechBook Transaction::Simple SimpleTable Complex Action FontDescriptor Procset Catalog FontEncoding Pages Destination Info Encryption Annotation Contents Outline Page Outlines Font ViewerPreferences Image Hash OHash QuickRef StdDev FontMetrics StrokeStyle ARC4 PolygonPoint ImageInfo lib/pdf/simpletable.rb lib/pdf/writer.rb lib/pdf/techbook.rb lib/pdf/quickref.rb lib/pdf/charts/stddev.rb Charts Math lib/pdf/writer/ohash.rb lib/pdf/writer/fontmetrics.rb lib/pdf/writer/strokestyle.rb lib/pdf/writer/arc4.rb lib/pdf/writer/graphics.rb lib/pdf/writer/object.rb lib/pdf/writer/object/image.rb External lib/pdf/writer/object/font.rb lib/pdf/writer/object/outlines.rb lib/pdf/writer/object/contents.rb lib/pdf/writer/object/annotation.rb lib/pdf/writer/object/destination.rb lib/pdf/writer/object/viewerpreferences.rb lib/pdf/writer/object/info.rb lib/pdf/writer/object/fontencoding.rb lib/pdf/writer/object/page.rb lib/pdf/writer/object/catalog.rb lib/pdf/writer/object/outline.rb lib/pdf/writer/object/encryption.rb lib/pdf/writer/object/procset.rb lib/pdf/writer/object/action.rb lib/pdf/writer/object/pages.rb lib/pdf/writer/object/fontdescriptor.rb Object OffsetReader EN Lang lib/pdf/writer/graphics/imageinfo.rb Graphics Writer 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.8'   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 2725
2725:   def _post_transaction_rewind
2726:     @objects.each { |e| e.instance_variable_set(:@parent,self) }
2727:   end

add content to the currently active object

[Source]

      # File lib/pdf/writer.rb, line 1029
1029:   def add_content(cc)
1030:     @current_contents << cc
1031:   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 1856
1856:   def add_destination(label, style, *params)
1857:     @destinations[label] = PDF::Writer::Object::Destination.new(self, @current_page, style, *params)
1858:   end

Add content to the documents info object.

[Source]

      # File lib/pdf/writer.rb, line 1805
1805:   def add_info(label, value = 0)
1806:       # This will only work if the label is one of the valid ones. Modify
1807:       # this so that arrays can be passed as well. If @label is an array
1808:       # then assume that it is key => value pairs else assume that they are
1809:       # both scalar, anything else will probably error.
1810:     if label.kind_of?(Hash)
1811:       label.each { |kk, vv| @info.__send__(kk.downcase.intern, vv) }
1812:     else
1813:       @info.__send__(label.downcase.intern, value)
1814:     end
1815:   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 1778
1778:   def add_object(id, where = :this_page)
1779:     obj = @loose_objects.detect { |ii| ii == id }
1780: 
1781:     if obj and @current_contents != obj
1782:       case where
1783:       when :all_pages, :this_page
1784:         @add_loose_objects[obj] = where if where == :all_pages
1785:         @current_contents.on_page.contents << obj if @current_contents.on_page
1786:       when :even_pages
1787:         @add_loose_objects[obj] = where
1788:         page = @current_contents.on_page
1789:         add_object(id) if (page.info.page_number % 2) == 0
1790:       when :odd_pages
1791:         @add_loose_objects[obj] = where
1792:         page = @current_contents.on_page
1793:         add_object(id) if (page.info.page_number % 2) == 1
1794:       when :all_following_pages
1795:         @add_loose_objects[obj] = :all_pages
1796:       when :following_even_pages
1797:         @add_loose_objects[obj] = :even_pages
1798:       when :following_odd_pages
1799:         @add_loose_objects[obj] = :odd_pages
1800:       end
1801:     end
1802:   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 1362
1362:   def add_text(x, y, text, size = nil, angle = 0, word_space_adjust = 0)
1363:     if text.kind_of?(Numeric) and size.kind_of?(String)
1364:       text, size = size, text
1365:       warn PDF::Writer::Lang[:add_text_parameters_reversed] % caller[0]
1366:     end
1367: 
1368:     if size.nil? or size <= 0
1369:       size = @font_size
1370:     end
1371: 
1372:     select_font("Helvetica") if @fonts.empty?
1373: 
1374:     text = text.to_s
1375: 
1376:       # If there are any open callbacks, then they should be called, to show
1377:       # the start of the line
1378:     @callbacks.reverse_each do |ii|
1379:       info = ii.dup
1380:       info[:x]      = x
1381:       info[:y]      = y
1382:       info[:angle]  = angle
1383:       info[:status] = :start_line
1384: 
1385:       info[:tag][self, info]
1386:     end
1387:     if angle == 0
1388:       add_content("\nBT %.3f %.3f Td" % [x, y])
1389:     else
1390:       rad = PDF::Math.deg2rad(angle)
1391:       tt = "\nBT %.3f %.3f %.3f %.3f %.3f %.3f Tm"
1392:       tt = tt % [ Math.cos(rad), Math.sin(rad), -Math.sin(rad), Math.cos(rad), x, y ]
1393:       add_content(tt)
1394:     end
1395: 
1396:     if (word_space_adjust != 0) or not ((@word_space_adjust.nil?) and (@word_space_adjust != word_space_adjust))
1397:       @word_space_adjust = word_space_adjust
1398:       add_content(" %.3f Tw" % word_space_adjust)
1399:     end
1400: 
1401:     pos = -1
1402:     start = 0
1403:     loop do
1404:       pos += 1
1405:       break if pos == text.size
1406:       font_change = true
1407:       tag_size, text, font_change = quick_text_tags(text, pos, font_change)
1408: 
1409:       if tag_size != 0
1410:         if pos > start
1411:           part = text[start, pos - start]
1412:           tt = " /F#{find_font(@current_font).font_id}"
1413:           tt << " %.1f Tf %d Tr" % [ size, @current_text_render_style ]
1414:           tt << " (#{PDF::Writer.escape(part)}) Tj"
1415:           add_content(tt)
1416:         end
1417: 
1418:         if font_change
1419:           current_font!
1420:         else
1421:           add_content(" ET")
1422:           xp = x
1423:           yp = y
1424:           tag_size, text, font_change, xp, yp = text_tags(text, pos, font_change, true, xp, yp, size, angle, word_space_adjust)
1425: 
1426:             # Restart the text object
1427:           if angle.zero?
1428:             add_content("\nBT %.3f %.3f Td" % [xp, yp])
1429:           else
1430:             rad = PDF::Math.deg2rad(angle)
1431:             tt = "\nBT %.3f %.3f %.3f %.3f %.3f %.3f Tm"
1432:             tt = tt % [ Math.cos(rad), Math.sin(rad), -Math.sin(rad), Math.cos(rad), xp, yp ]
1433:             add_content(tt)
1434:           end
1435: 
1436:           if (word_space_adjust != 0) or (word_space_adjust != @word_space_adjust)
1437:             @word_space_adjust = word_space_adjust
1438:             add_content(" %.3f Tw" % [word_space_adjust])
1439:           end
1440:         end
1441: 
1442:         pos += tag_size - 1
1443:         start = pos + 1
1444:       end
1445:     end
1446: 
1447:     if start < text.size
1448:       part = text[start..-1]
1449: 
1450:       tt = " /F#{find_font(@current_font).font_id}"
1451:       tt << " %.1f Tf %d Tr" % [ size, @current_text_render_style ]
1452:       tt << " (#{PDF::Writer.escape(part)}) Tj"
1453:       add_content(tt)
1454:     end
1455:     add_content(" ET")
1456: 
1457:       # XXX: Experimental fix.
1458:     @callbacks.reverse_each do |ii|
1459:       info = ii.dup
1460:       info[:x]      = x
1461:       info[:y]      = y
1462:       info[:angle]  = angle
1463:       info[:status] = :end_line
1464:       info[:tag][self, info]
1465:     end
1466:   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 1604
1604:   def add_text_wrap(x, y, width, text, size = nil, justification = :left, angle = 0, test = false)
1605:     if text.kind_of?(Numeric) and size.kind_of?(String)
1606:       text, size = size, text
1607:       warn PDF::Writer::Lang[:add_textw_parameters_reversed] % caller[0]
1608:     end
1609: 
1610:     if size.nil? or size <= 0
1611:       size = @font_size
1612:     end
1613: 
1614:       # Need to store the initial text state, as this will change during the
1615:       # width calculation, but will need to be re-set before printing, so
1616:       # that the chars work out right
1617:     t_CTS = @current_text_state.dup
1618: 
1619:     select_font("Helvetica") if @fonts.empty?
1620:     return "" if width <= 0
1621: 
1622:     w = brk = brkw = 0
1623:     font = @current_font
1624:     tw = width / size.to_f * 1000
1625: 
1626:     pos = -1
1627:     loop do
1628:       pos += 1
1629:       break if pos == text.size
1630:       font_change = true
1631:       tag_size, text, font_change = quick_text_tags(text, pos, font_change)
1632:       if tag_size != 0
1633:         if font_change
1634:           current_font!
1635:           font = @current_font
1636:         end
1637:         pos += (tag_size - 1)
1638:       else
1639:         w += char_width(font, text[pos, 1])
1640: 
1641:         if w > tw # We need to truncate this line
1642:           if brk > 0 # There is somewhere to break the line.
1643:             if text[brk] == " "
1644:               tmp = text[0, brk]
1645:             else
1646:               tmp = text[0, brk + 1]
1647:             end
1648:             x, adjust = adjust_wrapped_text(tmp, brkw, width, x, justification)
1649: 
1650:               # Reset the text state
1651:             @current_text_state = t_CTS.dup
1652:             current_font!
1653:             add_text(x, y, tmp, size, angle, adjust) unless test
1654:             return text[brk + 1..-1]
1655:           else # just break before the current character
1656:             tmp = text[0, pos]
1657: #           tmpw = (w - char_width(font, text[pos, 1])) * size / 1000.0
1658:             x, adjust = adjust_wrapped_text(tmp, brkw, width, x, justification)
1659: 
1660:               # Reset the text state
1661:             @current_text_state = t_CTS.dup
1662:             current_font!
1663:             add_text(x, y, tmp, size, angle, adjust) unless test
1664:             return text[pos..-1]
1665:           end
1666:         end
1667: 
1668:         if text[pos] == ?-
1669:           brk = pos
1670:           brkw = w * size / 1000.0
1671:         end
1672: 
1673:         if text[pos, 1] == " "
1674:           brk = pos
1675:           ctmp = text[pos]
1676:           ctmp = @fonts[font].differences[ctmp] unless @fonts[font].differences.nil?
1677:           z = @fonts[font].c[tmp].nil? ? 0 : @fonts[font].c[tmp]['WX']
1678:           brkw = (w - z) * size / 1000.0
1679:         end
1680:       end
1681:     end
1682: 
1683:       # There was no need to break this line.
1684:     justification = :left if justification == :full
1685:     tmpw = (w * size) / 1000.0
1686:     x, adjust = adjust_wrapped_text(text, tmpw, width, x, justification)
1687:       # reset the text state
1688:     @current_text_state = t_CTS.dup
1689:     current_font!
1690:     add_text(x, y, text, size, angle, adjust) unless test
1691:     return ""
1692:   end

Changes the insert_page property to append to the page set.

[Source]

      # File lib/pdf/writer.rb, line 2016
2016:   def append_page
2017:     insert_mode(:last)
2018:   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 1762
1762:   def close_object
1763:     unless @stack.empty?
1764:       obj = @stack.pop
1765:       @current_contents = obj[:contents]
1766:       @current_page = obj[:page]
1767:     end
1768:   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 1902
1902:   def columns?
1903:     @columns_on
1904:   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 997
 997:   def current_font!
 998:     select_font("Helvetica") unless @current_base_font
 999: 
1000:     font = File.basename(@current_base_font)
1001:     if @font_families[font] and @font_families[font][@current_text_state]
1002:         # Then we are in some state or another and this font has a family,
1003:         # and the current setting exists within it select the font, then
1004:         # return it.
1005:       if File.dirname(@current_base_font) != '.'
1006:         nf = File.join(File.dirname(@current_base_font), @font_families[font][@current_text_state])
1007:       else
1008:         nf = @font_families[font][@current_text_state]
1009:       end
1010: 
1011:       unless @fonts[nf]
1012:         enc = {
1013:           :encoding     => @fonts[font].encoding,
1014:           :differences  => @fonts[font].differences
1015:         }
1016:         load_font(nf, enc)
1017:       end
1018:       @current_font = nf
1019:     else
1020:       @current_font = @current_base_font
1021:     end
1022:   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 2111
2111:   def current_page_number
2112:     @pageset.size
2113:   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 1047
1047:   def font_descender(size = nil)
1048:     size = @font_size if size.nil? or size <= 0
1049: 
1050:     select_font("Helvetica") if @fonts.empty?
1051:     hi = @fonts[@current_font].fontbbox[1].to_f
1052:     (size * hi / 1000.0)
1053:   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 1035
1035:   def font_height(size = nil)
1036:     size = @font_size if size.nil? or size <= 0
1037: 
1038:     select_font("Helvetica") if @fonts.empty?
1039:     hh = @fonts[@current_font].fontbbox[3].to_f - @fonts[@current_font].fontbbox[1].to_f
1040:     (size * hh / 1000.0)
1041:   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 1980
1980:   def insert_mode(options = {})
1981:     case options
1982:     when :on, true
1983:       @insert_mode = true
1984:     when :off, false
1985:       @insert_mode = false
1986:     else
1987:       return @insert_mode unless options
1988: 
1989:       @insert_mode = options[:on] unless options[:on].nil?
1990: 
1991:       unless options[:page].nil?
1992:         if @pageset[options[:page]].nil? or options[:page] == :last
1993:           @insert_page = @pageset[-1]
1994:         else
1995:           @insert_page = @pageset[options[:page]]
1996:         end
1997:       end
1998: 
1999:       @insert_position = options[:position] if options[:position]
2000:     end
2001:   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 2007
2007:   def insert_page(page = nil)
2008:     return @insert_page unless page
2009:     if page == :last
2010:       @insert_page = @pageset[-1]
2011:     else
2012:       @insert_page = @pageset[page]
2013:     end
2014:   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 2025
2025:   def insert_position(position = nil)
2026:     return @insert_position unless position
2027:     @insert_position = position
2028:   end

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

[Source]

      # File lib/pdf/writer.rb, line 2446
2446:   def lines_remaining(font_size = nil)
2447:     font_size ||= @font_size
2448:     remaining = @y - @bottom_margin
2449:     remaining / font_height(font_size).to_f
2450:   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 2084
2084:   def new_page(insert = false, page = nil, pos = :after)
2085:     reset_state_at_page_finish
2086: 
2087:     if insert
2088:         # The id from the PDF::Writer class is the id of the contents of the
2089:         # page, not the page object itself. Query that object to find the
2090:         # parent.
2091:       _new_page = PDF::Writer::Object::Page.new(self, { :rpage => page, :pos => pos })
2092:     else
2093:       _new_page = PDF::Writer::Object::Page.new(self)
2094:     end
2095: 
2096:     reset_state_at_page_start
2097: 
2098:       # If there has been a stroke or fill color set, transfer them.
2099:     fill_color!
2100:     stroke_color!
2101:     stroke_style!
2102: 
2103:       # the call to the page object set @current_contents to the present page,
2104:       # so this can be returned as the page id
2105: #   @current_contents
2106:     _new_page
2107:   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 1829
1829:   def open_at(page, style, *params)
1830:     d = PDF::Writer::Object::Destination.new(self, page, style, *params)
1831:     @catalog.open_here = d
1832:   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 1821
1821:   def open_here(style, *params)
1822:     open_at(@current_page, style, *params)
1823:   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 2710
2710:   def open_new_object
2711:     save_state
2712:     oid = open_object
2713:     close_object
2714:     add_object(oid)
2715:     reopen_object(oid)
2716:     oid
2717:   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 1743
1743:   def open_object
1744:     @stack << { :contents => @current_contents, :page => @current_page }
1745:     @current_contents = PDF::Writer::Object::Contents.new(self)
1746:     @loose_objects << @current_contents
1747:     yield @current_contents if block_given?
1748:     @current_contents
1749:   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 1869
1869:   def page_mode=(mode)
1870:     @catalog.page_mode = value
1871:   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 1752
1752:   def reopen_object(id)
1753:     @stack << { :contents => @current_contents, :page => @current_page }
1754:     @current_contents = id
1755:       # if this object is the primary contents for a page, then set the
1756:       # current page to its parent
1757:     @current_page = @current_contents.on_page unless @current_contents.on_page.nil?
1758:     @current_contents
1759:   end

Restore a previously saved state.

[Source]

      # File lib/pdf/writer.rb, line 1721
1721:   def restore_state
1722:     unless @state_stack.empty?
1723:       state = @state_stack.pop
1724:       @current_fill_color         = state.fill_color
1725:       @current_stroke_color       = state.stroke_color
1726:       @current_text_render_style  = state.text_render_style
1727:       @current_stroke_style       = state.stroke_style
1728:       stroke_style!
1729:     end
1730:     add_content("\nQ")
1731:   end

Save the PDF as a file to disk.

[Source]

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

Saves the state.

[Source]

      # File lib/pdf/writer.rb, line 1695
1695:   def save_state
1696:     PDF::Writer::State.new do |state|
1697:       state.fill_color        = @current_fill_color
1698:       state.stroke_color      = @current_stroke_color
1699:       state.text_render_style = @current_text_render_style
1700:       state.stroke_style      = @current_stroke_style
1701:       @state_stack.push state
1702:     end
1703:     add_content("\nq")
1704:   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 975
975:   def select_font(font, encoding = nil)
976:     load_font(font, encoding) unless @fonts[font]
977: 
978:     @current_base_font = font
979:     current_font!
980:     @current_base_font
981:   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 1910
1910:   def start_columns(size = 2, gutter = 10)
1911:       # Start from the current y-position; make the set number of columns.
1912:     return false if @columns_on
1913: 
1914:     @columns = {
1915:       :current => 1,
1916:       :bot_y   => @y
1917:     }
1918:     @columns_on = true
1919:       # store the current margins
1920:     @columns[:left]   = @left_margin
1921:     @columns[:right]  = @right_margin
1922:     @columns[:top]    = @top_margin
1923:     @columns[:bottom] = @bottom_margin
1924:       # Reset the margins to suit the new columns. Safe enough to assume the
1925:       # first column here, but start from the current y-position.
1926:     @top_margin = @page_height - @y
1927:     @columns[:size]   = size   || 2
1928:     @columns[:gutter] = gutter || 10
1929:     w = absolute_right_margin - absolute_left_margin
1930:     @columns[:width] = (w - ((size - 1) * gutter)) / size.to_f
1931:     @right_margin = @page_width - (@left_margin + @columns[:width])
1932:   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 2034
2034:   def start_new_page(force = false)
2035:     page_required = true
2036: 
2037:     if @columns_on
2038:         # Check if this is just going to a new column. Increment the column
2039:         # number.
2040:       @columns[:current] += 1
2041: 
2042:       if @columns[:current] <= @columns[:size] and not force
2043:         page_required = false
2044:         @columns[:bot_y] = @y if @y < @columns[:bot_y]
2045:       else
2046:         @columns[:current] = 1
2047:         @top_margin = @columns[:top]
2048:         @columns[:bot_y] = absolute_top_margin
2049:       end
2050: 
2051:       w = @columns[:width]
2052:       g = @columns[:gutter]
2053:       n = @columns[:current] - 1
2054:       @left_margin = @columns[:left] + n * (g + w)
2055:       @right_margin = @page_width - (@left_margin + w)
2056:     end
2057: 
2058:     if page_required or force
2059:         # make a new page, setting the writing point back to the top.
2060:       @y = absolute_top_margin
2061:         # make the new page with a call to the basic class
2062:       if @insert_mode
2063:         id = new_page(true, @insert_page, @insert_position)
2064:         @pageset << id
2065:           # Manipulate the insert options so that inserted pages follow each
2066:           # other
2067:         @insert_page = id
2068:         @insert_position = :after
2069:       else
2070:         @pageset << new_page
2071:       end
2072: 
2073:     else
2074:       @y = absolute_top_margin
2075:     end
2076:     @pageset
2077:   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 2129
2129:   def start_page_numbering(x, y, size, pos = nil, pattern = nil, starting = nil)   
2130:     pos     ||= :left
2131:     pattern ||= "<PAGENUM> of <TOTALPAGENUM>"
2132:     starting  ||= 1
2133: 
2134:     @page_numbering ||= []
2135:     @page_numbering << (o = {})
2136: 
2137:     page    = @pageset.size - 1
2138:     o[page] = {
2139:       :x        => x,
2140:       :y        => y,
2141:       :pos      => pos,
2142:       :pattern  => pattern,
2143:       :starting => starting,
2144:       :size     => size,
2145:       :start    => true
2146:     }
2147:     @page_numbering.index(o)
2148:   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 1946
1946:   def stop_columns
1947:     return false unless @columns_on
1948:     @columns_on = false
1949: 
1950:     @columns[:bot_y] = @y if @y < @columns[:bot_y]
1951: 
1952:     if (@columns[:bot_y] > @bottom_margin) or @column_number == 1
1953:       @y = @columns[:bot_y]
1954:     else
1955:       start_new_page
1956:     end
1957:     restore_margins_after_columns
1958:     @columns = {}
1959:     true
1960:   end

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

[Source]

      # File lib/pdf/writer.rb, line 1771
1771:   def stop_object(id)
1772:     obj = @loose_objects.detect { |ii| ii.oid == id.oid }
1773:     @add_loose_objects[obj] = nil
1774:   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 2187
2187:   def stop_page_numbering(stop_total = false, stop_at = :current, scheme = 0)
2188:     return false unless @page_numbering
2189: 
2190:     page = @pageset.size - 1
2191: 
2192:     @page_numbering[scheme][page] ||= {}
2193:     o = @page_numbering[scheme][page]
2194: 
2195:     case [ stop_total, stop_at == :current ]
2196:     when [ true, true ]
2197:       o[:stop] = :stop_total
2198:     when [ true, false ]
2199:       o[:stop] = :stop_total_next
2200:     when [ false, true ]
2201:       o[:stop] = :stop_next
2202:       else
2203:       o[:stop] = :stop
2204:     end
2205:   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 2334
2334:   def text(text, options = {})
2335:       # Apply the filtering which will make underlining (and other items)
2336:       # function.
2337:     text = preprocess_text(text)
2338: 
2339:     options ||= {}
2340: 
2341:     new_page_required = false
2342:     __y = @y
2343: 
2344:     if options[:absolute_left]
2345:       left = options[:absolute_left]
2346:     else
2347:       left = @left_margin
2348:       left += options[:left] if options[:left]
2349:     end
2350: 
2351:     if options[:absolute_right]
2352:       right = options[:absolute_right]
2353:     else
2354:       right = absolute_right_margin
2355:       right -= options[:right] if options[:right]
2356:     end
2357: 
2358:     size = options[:font_size] || 0
2359:     if size <= 0
2360:       size = @font_size
2361:     else
2362:       @font_size = size
2363:     end
2364: 
2365:     just = options[:justification] || :left
2366: 
2367:     if options[:leading] # leading instead of spacing
2368:       height = options[:leading]
2369:     elsif options[:spacing]
2370:       height = options[:spacing] * font_height(size)
2371:     else
2372:       height = font_height(size)
2373:     end
2374: 
2375:     text.each do |line|
2376:       start = true
2377:       loop do # while not line.empty? or start
2378:         break if (line.nil? or line.empty?) and not start
2379: 
2380:         start = false
2381: 
2382:         @y -= height
2383: 
2384:         if @y < @bottom_margin
2385:           if options[:test]
2386:             new_page_required = true
2387:           else
2388:               # and then re-calc the left and right, in case they have
2389:               # changed due to columns
2390:             start_new_page
2391:             @y -= height
2392: 
2393:             if options[:absolute_left]
2394:               left = options[:absolute_left]
2395:             else
2396:               left = @left_margin
2397:               left += options[:left] if options[:left]
2398:             end
2399: 
2400:             if options[:absolute_right]
2401:               right = options[:absolute_right]
2402:             else
2403:               right = absolute_right_margin
2404:               right -= options[:right] if options[:right]
2405:             end
2406:           end
2407:         end
2408: 
2409:         line = add_text_wrap(left, @y, right - left, line, size, just, 0, options[:test])
2410:       end
2411:     end
2412: 
2413:     if options[:test]
2414:       @y = __y
2415:       new_page_required
2416:     else
2417:       @y
2418:     end
2419:   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 1489
1489:   def text_line_width(text, size = nil)
1490:     if text.kind_of?(Numeric) and size.kind_of?(String)
1491:       text, size = size, text
1492:       warn PDF::Writer::Lang[:text_width_parameters_reversed] % caller[0]
1493:     end
1494: 
1495:     if size.nil? or size <= 0
1496:       size = @font_size
1497:     end
1498: 
1499:       # This function should not change any of the settings, though it will
1500:       # need to track any tag which change during calculation, so copy them
1501:       # at the start and put them back at the end.
1502:     t_CTS = @current_text_state.dup
1503: 
1504:     select_font("Helvetica") if @fonts.empty?
1505:       # converts a number or a float to a string so it can get the width
1506:     tt = text.to_s
1507:       # hmm, this is where it all starts to get tricky - use the font
1508:       # information to calculate the width of each character, add them up
1509:       # and convert to user units
1510:     width = 0
1511:     font = @current_font
1512: 
1513:     pos = -1
1514:     loop do
1515:       pos += 1
1516:       break if pos == tt.size
1517:       font_change = true
1518:       tag_size, text, font_change = quick_text_tags(text, pos, font_change)
1519:       if tag_size != 0
1520:         if font_change
1521:           current_font!
1522:           font = @current_font
1523:         end
1524:         pos += tag_size - 1
1525:       else
1526:         if "&lt;" == tt[pos, 4]
1527:           width += char_width(font, '<')
1528:           pos += 3
1529:         elsif "&gt;" == tt[pos, 4]
1530:           width += char_width(font, '>')
1531:           pos += 3
1532:         elsif "&amp;" == tt[pos, 5]
1533:           width += char_width(font, '&')
1534:           pos += 4
1535:         else
1536:           width += char_width(font, tt[pos, 1])
1537:         end
1538:       end
1539:     end
1540: 
1541:     @current_text_state = t_CTS.dup
1542:     current_font!
1543: 
1544:     (width * size / 1000.0)
1545:   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 1554
1554:   def text_width(text, size = nil)
1555:     if text.kind_of?(Numeric) and size.kind_of?(String)
1556:       text, size = size, text
1557:       warn PDF::Writer::Lang[:text_width_parameters_reversed] % caller[0]
1558:     end
1559: 
1560:     if size.nil? or size <= 0
1561:       size = @font_size
1562:     end
1563: 
1564:     max   = 0
1565: 
1566:     text.to_s.each do |line|
1567:       width = text_line_width(line, size)
1568:       max = width if width > max
1569:     end
1570:     max
1571:   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 2157
2157:   def which_page_number(page_num, scheme = 0)
2158:     return nil unless @page_numbering
2159: 
2160:     num   = nil
2161:     start = start_num = 1
2162: 
2163:     @page_numbering[scheme].each do |kk, vv|
2164:       if kk <= page_num
2165:         if vv.kind_of?(Hash)
2166:           unless vv[:starting].nil?
2167:             start = vv[:starting]
2168:             start_num = kk
2169:             num = page_num - start_num + start
2170:           end
2171:         else
2172:           num = nil
2173:         end
2174:       end
2175:     end
2176:     num
2177:   end

Private Instance methods

[Source]

      # File lib/pdf/writer.rb, line 2214
2214:   def add_page_numbers            
2215:       # This will go through the @page_numbering array and add the page
2216:       # numbers are required.
2217:     if @page_numbering                           
2218:       page_count  = @pageset.size
2219:       pn_tmp      = @page_numbering.dup
2220: 
2221:         # Go through each of the page numbering schemes.
2222:       pn_tmp.each do |scheme|
2223:           # First, find the total pages for this schemes.
2224:         page = page_number_search(:stop_total, scheme)
2225: 
2226:         if page                             
2227:           total_pages = page
2228:         else
2229:           page = page_number_search(:stop_total_next, scheme)        
2230:           if page  
2231:             total_pages = page
2232:           else                     
2233:             total_pages = page_count - 1
2234:           end
2235:         end
2236: 
2237:         status  = nil
2238:         delta   = pattern = pos = x = y = size = nil 
2239:         pattern = pos = x = y = size = nil
2240: 
2241:         @pageset.each_with_index do |page, index|
2242:           next if status.nil? and scheme[index].nil?
2243: 
2244:           info = scheme[index]
2245:           if info
2246:             if info[:start]
2247:               status = true
2248:               if info[:starting] 
2249:                 delta = info[:starting] - index 
2250:               else 
2251:                 delta = index 
2252:               end  
2253: 
2254:               pattern = info[:pattern]
2255:               pos     = info[:pos]
2256:               x       = info[:x]
2257:               y       = info[:y]
2258:               size    = info[:size]
2259: 
2260:               # Check for the special case of page numbering starting and
2261:               # stopping on the same page.
2262:               status = :stop_next if info[:stop]
2263:             elsif [:stop, :stop_total].include?(info[:stop])
2264:               status = :stop_now
2265:             elsif status == true and [:stop_next, :stop_total_next].include?(info[:stop])
2266:               status = :stop_next
2267:             end
2268:           end
2269: 
2270:           if status
2271:               # Add the page numbering to this page
2272:             num   = index + delta.to_i 
2273:             total = total_pages + num - index
2274:             patt  = pattern.gsub(/<PAGENUM>/, num.to_s).gsub(/<TOTALPAGENUM>/, total.to_s)
2275:             reopen_object(page.contents.first)
2276: 
2277:             case pos
2278:             when :left    # Write the page number from x.
2279:               w = 0
2280:             when :right   # Write the page number to x.
2281:               w = text_width(patt, size)
2282:             when :center  # Write the page number around x.
2283:               w = text_width(patt, size) / 2.0
2284:             end
2285:             add_text(x - w, y, patt, size)
2286:             close_object
2287:             status = nil if [ :stop_now, :stop_next ].include?(status)
2288:           end
2289:         end
2290:       end
2291:     end
2292:   end

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

[Source]

      # File lib/pdf/writer.rb, line 1575
1575:   def adjust_wrapped_text(text, actual, width, x, just)
1576:     adjust  = 0
1577: 
1578:     case just
1579:     when :left
1580:       nil
1581:     when :right
1582:       x += (width - actual)
1583:     when :center
1584:       x += (width - actual) / 2.0
1585:     when :full
1586:       spaces = text.count(" ")
1587:       adjust = (width - actual) / spaces.to_f if spaces > 0
1588:     end
1589: 
1590:     [x, adjust]
1591:   end

[Source]

      # File lib/pdf/writer.rb, line 1468
1468:   def char_width(font, char)
1469:     char = char[0] unless @fonts[font].c[char]
1470: 
1471:     if @fonts[font].differences and @fonts[font].c[char].nil?
1472:       name = @fonts[font].differences[char] || 'M'
1473:       width = @fonts[font].c[name]['WX'] if @fonts[font].c[name]['WX']
1474:     elsif @fonts[font].c[char]
1475:       width = @fonts[font].c[char]['WX']
1476:     else
1477:       width = @fonts[font].c['M']['WX']
1478:     end
1479:     width
1480:   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 - first_char] = metrics.c[cname]['WX'] if metrics.c[cname]
827:         end
828:       end
829: 
830:       raise RuntimeError, 'Font metrics file (.afm) invalid - no charcters described' if first_char == -1 and last_char == 0
831: 
832:       widthid = PDF::Writer::Object::Contents.new(self, :raw)
833:       widthid << "["
834:       (first_char .. last_char).each do |ii|
835:         if widths.has_key?(ii)
836:           widthid << " #{widths[ii].to_i}"
837:         else
838:           widthid << " 0"
839:         end
840:       end
841:       widthid << "]"
842: 
843:         # Load the pfb file, and put that into an object too. Note that PDF
844:         # supports only binary format Type1 font files and TrueType font
845:         # files. There is a simple utility to convert Type1 from pfa to pfb.
846:       data = File.open(fontfile, "rb") { |ff| ff.read }
847: 
848:         # Create the font descriptor.
849:       fdsc = PDF::Writer::Object::FontDescriptor.new(self)
850:         # Raw contents causes problems with Acrobat Reader.
851:       pfbc = PDF::Writer::Object::Contents.new(self)
852: 
853:         # Determine flags (more than a little flakey, hopefully will not
854:         # matter much).
855:       flags = 0
856:       if encoding == "none"
857:         flags += 2 ** 2
858:       else
859:         flags += 2 ** 6 if metrics.italicangle.nonzero?
860:         flags += 2 ** 0 if metrics.isfixedpitch == "true"
861:         flags += 2 ** 5 # Assume a non-symbolic font
862:       end
863: 
864:         # 1: FixedPitch:  All glyphs have the same width (as opposed to
865:         #                 proportional or variable-pitch fonts, which have
866:         #                 different widths).
867:         # 2: Serif:       Glyphs have serifs, which are short strokes drawn
868:         #                 at an angle on the top and bottom of glyph stems.
869:         #                 (Sans serif fonts do not have serifs.)
870:         # 3: Symbolic     Font contains glyphs outside the Adobe standard
871:         #                 Latin character set. This flag and the Nonsymbolic
872:         #                 flag cannot both be set or both be clear (see
873:         #                 below).
874:         # 4: Script:      Glyphs resemble cursive handwriting.
875:         # 6: Nonsymbolic: Font uses the Adobe standard Latin character set
876:         #                 or a subset of it (see below).
877:         # 7: Italic:      Glyphs have dominant vertical strokes that are
878:         #                 slanted.
879:         # 17: AllCap:     Font contains no lowercase letters; typically used
880:         #                 for display purposes, such as for titles or
881:         #                 headlines.
882:         # 18: SmallCap:   Font contains both uppercase and lowercase
883:         #                 letters. The uppercase letters are similar to
884:         #                 those in the regular version of the same typeface
885:         #                 family. The glyphs for the lowercase letters have
886:         #                 the same shapes as the corresponding uppercase
887:         #                 letters, but they are sized and their proportions
888:         #                 adjusted so that they have the same size and
889:         #                 stroke weight as lowercase glyphs in the same
890:         #                 typeface family.
891:         # 19: ForceBold:  See below.
892: 
893:       list = {
894:         'Ascent'      => 'Ascender',
895:         'CapHeight'   => 'CapHeight',
896:         'Descent'     => 'Descender',
897:         'FontBBox'    => 'FontBBox',
898:         'ItalicAngle' => 'ItalicAngle'
899:       }
900:       fdopt = {
901:         'Flags'     => flags,
902:         'FontName'  => metrics.fontname,
903:         'StemV'     => 100 # Don't know what the value for this should be!
904:       }
905: 
906:       list.each do |kk, vv|
907:         zz = metrics.__send__(vv.downcase.intern)
908:         fdopt[kk] = zz if zz
909:       end
910: 
911:         # Determine the cruicial lengths within this file
912:       if fontfile =~ /\.pfb$/o
913:         fdopt['FontFile'] = pfbc.oid
914:         i1 = data.index('eexec') + 6
915:         i2 = data.index('00000000')  - i1
916:         i3 = data.size - i2 - i1
917:         pfbc.add('Length1' => i1, 'Length2' => i2, 'Length3' => i3)
918:       elsif fontfile =~ /\.ttf$/o
919:         fdopt['FontFile2'] = pfbc.oid
920:         pfbc.add('Length1' => data.size)
921:       end
922: 
923:       fdsc.options = fdopt
924:         # Embed the font program
925:       pfbc << data
926: 
927:       # Tell the font object about all this new stuff
928:       tmp = {
929:         'BaseFont'        => metrics.fontname,
930:         'Widths'          => widthid.oid,
931:         'FirstChar'       => first_char,
932:         'LastChar'        => last_char,
933:         'FontDescriptor'  => fdsc.oid
934:       }
935:       tmp['SubType'] = 'TrueType' if fontfile =~ /\.ttf/
936: 
937:       tmp.each { |kk, vv| wfo.__send__("#{kk.downcase}=".intern, vv) }
938:     end
939: 
940:       # Also set the differences here. Note that this means that these will
941:       # take effect only the first time that a font is selected, else they
942:       # are ignored.
943:     metrics.differences = encoding_diff unless encoding_diff.nil?
944:     metrics.encoding = encoding_name
945:     metrics
946:   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 2207
2207:   def page_number_search(condition, scheme)
2208:     res = nil
2209:     scheme.each { |page, value| res = page if value[:stop] == condition }
2210:     res
2211:   end

[Source]

      # File lib/pdf/writer.rb, line 1346
1346:   def parse_tag_params(params)
1347:     params ||= ""
1348:     ph = {}
1349:     params.scan(TAG_PARAM_RE) do |param|
1350:       ph[param[0]] = param[1] || param[2] || param[3]
1351:     end
1352:     ph
1353:   end

[Source]

      # File lib/pdf/writer.rb, line 2295
2295:   def preprocess_text(text)
2296:     text
2297:   end

Wrapper function for text_tags

[Source]

      # File lib/pdf/writer.rb, line 1066
1066:   def quick_text_tags(text, ii, font_change)
1067:     ret = text_tags(text, ii, font_change)
1068:     [ret[0], ret[1], ret[2]]
1069:   end

Restore the state at the end of a page.

[Source]

      # File lib/pdf/writer.rb, line 1734
1734:   def reset_state_at_page_finish
1735:     add_content("\nQ" * @state_stack.size)
1736:   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 1709
1709:   def reset_state_at_page_start
1710:     @state_stack.each do |state|
1711:       fill_color!         state.fill_color
1712:       stroke_color!       state.stroke_color
1713:       text_render_style!  state.text_render_style
1714:       stroke_style!       state.stroke_style
1715:       add_content("\nq")
1716:     end
1717:   end

[Source]

      # File lib/pdf/writer.rb, line 1934
1934:   def restore_margins_after_columns
1935:     @left_margin   = @columns[:left]
1936:     @right_margin  = @columns[:right]
1937:     @top_margin    = @columns[:top]
1938:     @bottom_margin = @columns[:bottom]
1939:   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 1057
1057:   def text_end_position(x, y, angle, size, wa, text)
1058:     width = text_width(text, size)
1059:     width += wa * (text.count(" "))
1060:     rad = PDF::Math.deg2rad(angle)
1061:     [Math.cos(rad) * width + x, ((-Math.sin(rad)) * width + y)]
1062:   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 1180
1180:   def text_tags(text, pos, font_change, final = false, x = 0, y = 0, size = 0, angle = 0, word_space_adjust = 0)
1181:     tag_size = 0
1182: 
1183:     tag_match = %r!^<(/)?([^>]+)>!.match(text[pos..-1])
1184: 
1185:     if tag_match
1186:       closed, tag_name = tag_match.captures
1187:       cts = @current_text_state # Alias for shorter lines.
1188:       tag_size = tag_name.size + 2 + (closed ? 1 : 0)
1189: 
1190:       case tag_name
1191:       when %r{^(?:b|strong)$}o
1192:         if closed
1193:           cts.slice!(-1, 1) if ?b == cts[-1]
1194:         else
1195:           cts << ?b
1196:         end
1197:       when %r{^(?:i|em)$}o
1198:         if closed
1199:           cts.slice!(-1, 1) if ?i == cts[-1]
1200:         else
1201:           cts << ?i
1202:         end
1203:       when %r{^r:}o
1204:         _match = MATCH_TAG_REPLACE_RE.match(tag_name)
1205:         if _match.nil?
1206:           warn PDF::Writer::Lang[:callback_warning] % [ 'r:', tag_name ]
1207:           tag_size = 0
1208:         else
1209:           func    = _match.captures[0]
1210:           params  = parse_tag_params(_match.captures[1] || "")
1211:           tag     = TAGS[:replace][func]
1212: 
1213:           if tag
1214:             text[pos, tag_size] = tag[self, params]
1215:             tag_size, text, font_change, x, y = text_tags(text, pos,
1216:                                                           font_change,
1217:                                                           final, x, y, size,
1218:                                                           angle,
1219:                                                           word_space_adjust)
1220:           else
1221:             warn PDF::Writer::Lang[:callback_warning] % [ 'r:', func ]
1222:             tag_size = 0
1223:           end
1224:         end
1225:       when %r{^C:}o
1226:         _match = MATCH_TAG_DRAW_ONE_RE.match(tag_name)
1227:         if _match.nil?
1228:           warn PDF::Writer::Lang[:callback_warning] % [ 'C:', tag_name ]
1229:           tag_size = 0
1230:         else
1231:           func    = _match.captures[0]
1232:           params  = parse_tag_params(_match.captures[1] || "")
1233:           tag     = TAGS[:single][func]
1234: 
1235:           if tag
1236:             font_change = false
1237: 
1238:             if final
1239:               # Only call the function if this is the "final" call. Assess
1240:               # the text position. Calculate the text width to this point.
1241:               x, y = text_end_position(x, y, angle, size, word_space_adjust,
1242:                                        text[0, pos])
1243:               info = {
1244:                 :x          => x,
1245:                 :y          => y,
1246:                 :angle      => angle,
1247:                 :params     => params,
1248:                 :status     => :start,
1249:                 :cbid       => @callbacks.size + 1,
1250:                 :callback   => func,
1251:                 :height     => font_height(size),
1252:                 :descender  => font_descender(size)
1253:               }
1254: 
1255:               ret = tag[self, info]
1256:               if ret.kind_of?(Hash)
1257:                 ret.each do |rk, rv|
1258:                   x           = rv if rk == :x
1259:                   y           = rv if rk == :y
1260:                   font_change = rv if rk == :font_change
1261:                 end
1262:               end
1263:             end
1264:           else
1265:             warn PDF::Writer::Lang[:callback_Warning] % [ 'C:', func ]
1266:             tag_size = 0
1267:           end
1268:         end
1269:       when %r{^c:}o
1270:         _match = MATCH_TAG_DRAW_PAIR_RE.match(tag_name)
1271: 
1272:         if _match.nil?
1273:           warn PDF::Writer::Lang[:callback_warning] % [ 'c:', tag_name ]
1274:           tag_size = 0
1275:         else
1276:           func    = _match.captures[0]
1277:           params  = parse_tag_params(_match.captures[1] || "")
1278:           tag     = TAGS[:pair][func]
1279: 
1280:           if tag
1281:             font_change = false
1282: 
1283:             if final
1284:                 # Only call the function if this is the "final" call. Assess
1285:                 # the text position. Calculate the text width to this point.
1286:               x, y = text_end_position(x, y, angle, size, word_space_adjust,
1287:                                        text[0, pos])
1288:               info = {
1289:                 :x          => x,
1290:                 :y          => y,
1291:                 :angle      => angle,
1292:                 :params     => params,
1293:               }
1294: 
1295:               if closed
1296:                 info[:status] = :end
1297:                 info[:cbid]   = @callbacks.size
1298: 
1299:                 ret = tag[self, info]
1300: 
1301:                 if ret.kind_of?(Hash)
1302:                   ret.each do |rk, rv|
1303:                     x           = rv if rk == :x
1304:                     y           = rv if rk == :y
1305:                     font_change = rv if rk == :font_change
1306:                   end
1307:                 end
1308: 
1309:                 @callbacks.pop
1310:               else
1311:                 info[:status]     = :start
1312:                 info[:cbid]       = @callbacks.size + 1
1313:                 info[:tag]        = tag
1314:                 info[:callback]   = func
1315:                 info[:height]     = font_height(size)
1316:                 info[:descender]  = font_descender(size)
1317: 
1318:                 @callbacks << info
1319: 
1320:                 ret = tag[self, info]
1321: 
1322:                 if ret.kind_of?(Hash)
1323:                   ret.each do |rk, rv|
1324:                     x           = rv if rk == :x
1325:                     y           = rv if rk == :y
1326:                     font_change = rv if rk == :font_change
1327:                   end
1328:                 end
1329:               end
1330:             end
1331:           else
1332:             warn PDF::Writer::Lang[:callback_warning] % [ 'c:', func ]
1333:             tag_size = 0
1334:           end
1335:         end
1336:       else
1337:         tag_size = 0
1338:       end
1339:     end
1340:     [ tag_size, text, font_change, x, y ]
1341:   end

[Validate]