Class: PDF::Writer

Inherits:
Object
  • Object
show all
Includes:
Graphics, Transaction::Simple
Defined in:
lib/pdf/writer/state.rb,
lib/pdf/writer.rb,
lib/pdf/writer.rb

Overview

– PDF::Writer for Ruby.

http://rubyforge.org/projects/ruby-pdf/
Copyright 2003 - 2005 Austin Ziegler.

Licensed under a MIT-style licence. See LICENCE in the main distribution
for full licensing information.

$Id: state.rb 50 2005-05-16 03:59:21Z austin $ ++

Direct Known Subclasses

TechBook

Defined Under Namespace

Modules: Graphics, OffsetReader Classes: ARC4, Complex, External, FontMetrics, OHash, Object, PolygonPoint, State, StateStack, StrokeStyle, TagAlink, TagBullet, TagDisc, TagIlink, TagUline

Constant Summary collapse

VERSION =

The version of PDF::Writer.

'1.1.8'
FONT_PATH =

The system font path. The sytem font path will be determined differently for each operating system.

Win32

Uses ENV/Fonts as the system font path. There is an extension that will handle this better, but until and unless it is distributed with the standard Ruby Windows installer, PDF::Writer will not depend upon it.

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 =

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

{ # :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]
}
PDF_VERSION_13 =
'1.3'
PDF_VERSION_14 =
'1.4'
PDF_VERSION_15 =
'1.5'
PDF_VERSION_16 =
'1.6'
ENCRYPT_OPTIONS =

Standard encryption/DRM options.

{ #:nodoc:
  :print  => 4,
  :modify => 8,
  :copy   => 16,
  :add    => 32
}
MATCH_TAG_REPLACE_RE =

Matches tags.

%r{^r:(\w+)(?: (.*?))? */}
MATCH_TAG_DRAW_ONE_RE =

:nodoc:

%r{^C:(\w+)(?: (.*?))? */}
MATCH_TAG_DRAW_PAIR_RE =

:nodoc:

%r{^c:(\w+)(?: (.*))? *}
TAG_PARAM_RE =

:nodoc:

%r{(\w+)=(?:"([^"]+)"|'([^']+)'|(\w+))}
TAGS =

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

{
  :pair     => { },
  :single   => { },
  :replace  => { }
}

Constants included from Graphics

Graphics::KAPPA

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Graphics

#add_image, #add_image_from_file, #circle_at, #close, #close_fill, #close_fill_stroke, #close_stroke, #curve, #curve_to, #ecurve, #ecurve_to, #ellipse2_at, #ellipse_at, #fill, #fill_color, #fill_color!, #fill_color?, #fill_stroke, #image, #line, #line_to, #move_to, #polygon, #rectangle, #rotate_axis, #rounded_rectangle, #scale_axis, #scurve, #scurve_to, #segment_at, #skew_axis, #star, #stroke, #stroke_color, #stroke_color!, #stroke_color?, #stroke_style, #stroke_style!, #stroke_style?, #text_render_style, #text_render_style!, #text_render_style?, #transform_matrix, #translate_axis

Constructor Details

#initialize(options = {}) {|_self| ... } ⇒ Writer

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.

Yields:

  • (_self)

Yield Parameters:

  • _self (PDF::Writer)

    the object that the method was called on



325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
# File 'lib/pdf/writer.rb', line 325

def initialize(options = {})
  paper       = options[:paper] || "LETTER"
  orientation = options[:orientation] || :portrait
  version     = options[:version] || PDF_VERSION_13

  @mutex = Mutex.new
  @current_id = @current_font_id = 0

    # Start the document
  @objects              = []
  @callbacks            = []
  @font_families        = {}
  @fonts                = {}
  @stack                = []
  @state_stack          = StateStack.new
  @loose_objects        = []
  @current_text_state   = ""
  @options              = {}
  @destinations         = {}
  @add_loose_objects    = {}
  @images               = []
  @word_space_adjust    = nil
  @current_stroke_style = PDF::Writer::StrokeStyle.new(1)
  @page_numbering       = nil
  @arc4                 = nil
  @encryption           = nil
  @file_identifier      = nil

  @columns              = {}
  @columns_on           = false
  @insert_mode          = nil

  @catalog  = PDF::Writer::Object::Catalog.new(self)
  @outlines = PDF::Writer::Object::Outlines.new(self)
  @pages    = PDF::Writer::Object::Pages.new(self)

  @current_node	= @pages
  @procset  = PDF::Writer::Object::Procset.new(self)
  @info     = PDF::Writer::Object::Info.new(self)
  @page     = PDF::Writer::Object::Page.new(self)
  @current_text_render_style  = 0
  @first_page     = @page

  @version        = version

    # Initialize the default font families.
  init_font_families

  @font_size = 10
  @pageset = [@pages.first_page]

  if paper.kind_of?(Array)
    if paper.size == 4
      size = paper # Coordinate Array
    else
      size = [0, 0, PDF::Writer.cm2pts(paper[0]), PDF::Writer.cm2pts(paper[1])]
        # Paper size in centimeters has been passed
    end
  else
    size = PAGE_SIZES[paper.upcase].dup
  end
  size[3], size[2] = size[2], size[3] if orientation == :landscape

  @pages.media_box  = size

  @page_width       = size[2] - size[0]
  @page_height      = size[3] - size[1]
  @y = @page_height

    # Also set the margins to some reasonable defaults -- 1.27 cm, 36pt,
    # or 0.5 inches.
  margins_pt(36)

    # Set the current writing position to the top of the first page
  @y = absolute_top_margin
    # Get the ID of the page that was created during the instantiation
    # process.

  fill_color!   Color::RGB::Black
  stroke_color! Color::RGB::Black

  yield self if block_given?
end

Instance Attribute Details

#absolute_bottom_marginObject (readonly)

:nodoc:



485
486
487
# File 'lib/pdf/writer.rb', line 485

def absolute_bottom_margin
  @absolute_bottom_margin
end

#absolute_left_marginObject (readonly)

:nodoc:



470
471
472
# File 'lib/pdf/writer.rb', line 470

def absolute_left_margin
  @absolute_left_margin
end

#absolute_right_marginObject (readonly)

:nodoc:



475
476
477
# File 'lib/pdf/writer.rb', line 475

def absolute_right_margin
  @absolute_right_margin
end

#absolute_top_marginObject (readonly)

:nodoc:



480
481
482
# File 'lib/pdf/writer.rb', line 480

def absolute_top_margin
  @absolute_top_margin
end

#absolute_x_middleObject (readonly)

:nodoc:



501
502
503
# File 'lib/pdf/writer.rb', line 501

def absolute_x_middle
  @absolute_x_middle
end

#absolute_y_middleObject (readonly)

:nodoc:



506
507
508
# File 'lib/pdf/writer.rb', line 506

def absolute_y_middle
  @absolute_y_middle
end

#arc4Object (readonly)

The ARC4 encryption object. This is of no interest to external consumers.



132
133
134
# File 'lib/pdf/writer.rb', line 132

def arc4
  @arc4
end

#bottom_marginObject

Returns the value of attribute bottom_margin.



465
466
467
# File 'lib/pdf/writer.rb', line 465

def bottom_margin
  @bottom_margin
end

#catalogObject

The document catalog object (PDF::Writer::Object::Catalog). The options in the catalog should be set with PDF::Writer#open_here, PDF::Writer#viewer_preferences, and PDF::Writer#page_mode.

This is of little interest to external clients.



422
423
424
# File 'lib/pdf/writer.rb', line 422

def catalog
  @catalog
end

#column_countObject (readonly)

:nodoc:



1896
1897
1898
# File 'lib/pdf/writer.rb', line 1896

def column_count
  @column_count
end

#column_gutterObject (readonly)

:nodoc:



1884
1885
1886
# File 'lib/pdf/writer.rb', line 1884

def column_gutter
  @column_gutter
end

#column_numberObject (readonly)

:nodoc:



1890
1891
1892
# File 'lib/pdf/writer.rb', line 1890

def column_number
  @column_number
end

#column_widthObject (readonly)

:nodoc:



1877
1878
1879
# File 'lib/pdf/writer.rb', line 1877

def column_width
  @column_width
end

#compressedObject

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.



433
434
435
# File 'lib/pdf/writer.rb', line 433

def compressed
  @compressed
end

#current_base_fontObject (readonly)

Returns the value of attribute current_base_font.



1025
1026
1027
# File 'lib/pdf/writer.rb', line 1025

def current_base_font
  @current_base_font
end

#current_contentsObject (readonly)

Returns the current contents object to which raw PDF instructions may be written.



453
454
455
# File 'lib/pdf/writer.rb', line 453

def current_contents
  @current_contents
end

#current_fontObject (readonly)

Returns the value of attribute current_font.



1024
1025
1026
# File 'lib/pdf/writer.rb', line 1024

def current_font
  @current_font
end

#current_pageObject

The current page for writing. This is of little interest to external clients.



450
451
452
# File 'lib/pdf/writer.rb', line 450

def current_page
  @current_page
end

#destinationsObject (readonly)

The set of known labelled destinations. All destinations are of class PDF::Writer::Object::Destination. This is of little interest to external clients.



444
445
446
# File 'lib/pdf/writer.rb', line 444

def destinations
  @destinations
end

#encryption_keyObject

The string that will be used to encrypt this PDF document.



134
135
136
# File 'lib/pdf/writer.rb', line 134

def encryption_key
  @encryption_key
end

#first_pageObject (readonly)

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.



601
602
603
# File 'lib/pdf/writer.rb', line 601

def first_page
  @first_page
end

#font_familiesObject (readonly)

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.



620
621
622
# File 'lib/pdf/writer.rb', line 620

def font_families
  @font_families
end

#font_sizeObject

Returns the value of attribute font_size.



1026
1027
1028
# File 'lib/pdf/writer.rb', line 1026

def font_size
  @font_size
end

#infoObject (readonly)

The PDF::Writer::Object::Info info object. This is used to provide certain metadata.



447
448
449
# File 'lib/pdf/writer.rb', line 447

def info
  @info
end

#left_marginObject

Returns the value of attribute left_margin.



462
463
464
# File 'lib/pdf/writer.rb', line 462

def left_margin
  @left_margin
end

#margin_heightObject (readonly)

:nodoc:



491
492
493
# File 'lib/pdf/writer.rb', line 491

def margin_height
  @margin_height
end

#margin_widthObject (readonly)

:nodoc:



496
497
498
# File 'lib/pdf/writer.rb', line 496

def margin_width
  @margin_width
end

#margin_x_middleObject (readonly)

:nodoc:



511
512
513
# File 'lib/pdf/writer.rb', line 511

def margin_x_middle
  @margin_x_middle
end

#margin_y_middleObject (readonly)

:nodoc:



516
517
518
# File 'lib/pdf/writer.rb', line 516

def margin_y_middle
  @margin_y_middle
end

#objectsObject (readonly)

Contains all of the PDF objects, ready for final assembly. This is of no interest to external consumers.



128
129
130
# File 'lib/pdf/writer.rb', line 128

def objects
  @objects
end

#outlinesObject (readonly)

The PDF::Writer::Object::Outlines object. This is currently used very little. This is of little interest to external clients.



456
457
458
# File 'lib/pdf/writer.rb', line 456

def outlines
  @outlines
end

#page_heightObject (readonly)

Returns the value of attribute page_height.



467
468
469
# File 'lib/pdf/writer.rb', line 467

def page_height
  @page_height
end

#page_widthObject (readonly)

Returns the value of attribute page_width.



466
467
468
# File 'lib/pdf/writer.rb', line 466

def page_width
  @page_width
end

#pagesObject

The PDF::Writer::Object::Pages object. This is of little interest to external clients.



425
426
427
# File 'lib/pdf/writer.rb', line 425

def pages
  @pages
end

#pagesetObject (readonly)

The complete set of page objects. This is of little interest to external consumers.



460
461
462
# File 'lib/pdf/writer.rb', line 460

def pageset
  @pageset
end

#pointerObject

The vertical position of the writing point. If the vertical position is outside of the bottom margin, a new page will be created.



534
535
536
# File 'lib/pdf/writer.rb', line 534

def pointer
  @pointer
end

#procsetObject

The PDF::Writer::Object::Procset object. This is of little interest to external clients.



429
430
431
# File 'lib/pdf/writer.rb', line 429

def procset
  @procset
end

#right_marginObject

Returns the value of attribute right_margin.



463
464
465
# File 'lib/pdf/writer.rb', line 463

def right_margin
  @right_margin
end

#top_marginObject

Returns the value of attribute top_margin.



464
465
466
# File 'lib/pdf/writer.rb', line 464

def top_margin
  @top_margin
end

#versionObject (readonly)

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.



416
417
418
# File 'lib/pdf/writer.rb', line 416

def version
  @version
end

#yObject

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.



525
526
527
# File 'lib/pdf/writer.rb', line 525

def y
  @y
end

Class Method Details

.cm2pts(x) ⇒ Object

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



226
227
228
# File 'lib/pdf/writer.rb', line 226

def cm2pts(x)
  (x / 2.54) * 72
end

.escape(text) ⇒ Object

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



26
27
28
29
30
31
32
33
# File 'lib/pdf/writer.rb', line 26

def self.escape(text)
  text.gsub(/\\/, '\\\\\\\\').
       gsub(/\(/, '\\(').
       gsub(/\)/, '\\)').
       gsub(/&lt;/, '<').
       gsub(/&gt;/, '>').
       gsub(/&amp;/, '&')
end

.in2pts(x) ⇒ Object

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



238
239
240
# File 'lib/pdf/writer.rb', line 238

def in2pts(x)
  x * 72
end

.mm2pts(x) ⇒ Object

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



232
233
234
# File 'lib/pdf/writer.rb', line 232

def mm2pts(x)
  (x / 25.4) * 72
end

.parse_fonts_conf(filename) ⇒ Object

Parse the fonts.conf XML file.



94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/pdf/writer.rb', line 94

def parse_fonts_conf(filename)
  doc = REXML::Document.new(File.open(filename, "rb")).root rescue nil

  if doc
    path = REXML::XPath.match(doc, '//dir').map do |el|
      el.text.gsub($/, '')
    end
    doc = nil
  else
    path = []
  end
  path
end

.prepress(options = { }) {|pdf| ... } ⇒ Object

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.

Yields:

  • (pdf)


169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/pdf/writer.rb', line 169

def prepress(options = { })
  pdf = self.new(options)

  bleed_size  = options[:bleed_size] || 12
  mark_length = options[:mark_length] || 18

  pdf.left_margin   = options[:left_margin] if options[:left_margin]
  pdf.right_margin  = options[:right_margin] if options[:right_margin]
  pdf.top_margin    = options[:top_margin] if options[:top_margin]
  pdf.bottom_margin = options[:bottom_margin] if options[:bottom_margin]

  # This is in an "odd" order because the y-coordinate system in PDF
  # is from bottom to top.
  tx0 = pdf.pages.media_box[0] + pdf.left_margin
  ty0 = pdf.pages.media_box[3] - pdf.top_margin
  tx1 = pdf.pages.media_box[2] - pdf.right_margin
  ty1 = pdf.pages.media_box[1] + pdf.bottom_margin

  bx0 = tx0 - bleed_size
  by0 = ty0 - bleed_size
  bx1 = tx1 + bleed_size
  by1 = ty1 + bleed_size

  pdf.pages.trim_box  = [ tx0, ty0, tx1, ty1 ]
  pdf.pages.bleed_box = [ bx0, by0, bx1, by1 ]

  all = pdf.open_object
  pdf.save_state
  kk = Color::CMYK.new(0, 0, 0, 100)
  pdf.stroke_color! kk
  pdf.fill_color! kk
  pdf.stroke_style! StrokeStyle.new(0.3)

  pdf.prepress_clip_mark(tx1, ty0,   0, mark_length, bleed_size)  # Upper Right
  pdf.prepress_clip_mark(tx0, ty0,  90, mark_length, bleed_size)  # Upper Left
  pdf.prepress_clip_mark(tx0, ty1, 180, mark_length, bleed_size)  # Lower Left
  pdf.prepress_clip_mark(tx1, ty1, -90, mark_length, bleed_size)  # Lower Right

  mid_x = pdf.pages.media_box[2] / 2.0
  mid_y = pdf.pages.media_box[3] / 2.0

  pdf.prepress_center_mark(mid_x, ty0,   0, mark_length, bleed_size) # Centre Top
  pdf.prepress_center_mark(tx0, mid_y,  90, mark_length, bleed_size) # Centre Left
  pdf.prepress_center_mark(mid_x, ty1, 180, mark_length, bleed_size) # Centre Bottom
  pdf.prepress_center_mark(tx1, mid_y, -90, mark_length, bleed_size) # Centre Right

  pdf.restore_state
  pdf.close_object
  pdf.add_object(all, :all)

  yield pdf if block_given?

  pdf
end

Instance Method Details

#_post_transaction_rewindObject

memory improvement for transaction-simple



2725
2726
2727
# File 'lib/pdf/writer.rb', line 2725

def _post_transaction_rewind
  @objects.each { |e| e.instance_variable_set(:@parent,self) }
end

#add_content(cc) ⇒ Object

add content to the currently active object



1029
1030
1031
# File 'lib/pdf/writer.rb', line 1029

def add_content(cc)
  @current_contents << cc
end

#add_destination(label, style, *params) ⇒ Object

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.



1856
1857
1858
# File 'lib/pdf/writer.rb', line 1856

def add_destination(label, style, *params)
  @destinations[label] = PDF::Writer::Object::Destination.new(self, @current_page, style, *params)
end

#add_info(label, value = 0) ⇒ Object

Add content to the documents info object.



1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
# File 'lib/pdf/writer.rb', line 1805

def add_info(label, value = 0)
    # This will only work if the label is one of the valid ones. Modify
    # this so that arrays can be passed as well. If @label is an array
    # then assume that it is key => value pairs else assume that they are
    # both scalar, anything else will probably error.
  if label.kind_of?(Hash)
    label.each { |kk, vv| @info.__send__(kk.downcase.intern, vv) }
  else
    @info.__send__(label.downcase.intern, value)
  end
end

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



681
682
683
# File 'lib/pdf/writer.rb', line 681

def add_internal_link(label, x0, y0, x1, y1)
  PDF::Writer::Object::Annotation.new(self, :ilink, [x0, y0, x1, y1], label)
end

Add a link in the document to an external URL.



675
676
677
# File 'lib/pdf/writer.rb', line 675

def add_link(uri, x0, y0, x1, y1)
  PDF::Writer::Object::Annotation.new(self, :link, [x0, y0, x1, y1], uri)
end

#add_object(id, where = :this_page) ⇒ Object

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



1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
# File 'lib/pdf/writer.rb', line 1778

def add_object(id, where = :this_page)
  obj = @loose_objects.detect { |ii| ii == id }

  if obj and @current_contents != obj
    case where
    when :all_pages, :this_page
      @add_loose_objects[obj] = where if where == :all_pages
      @current_contents.on_page.contents << obj if @current_contents.on_page
    when :even_pages
      @add_loose_objects[obj] = where
      page = @current_contents.on_page
      add_object(id) if (page.info.page_number % 2) == 0
    when :odd_pages
      @add_loose_objects[obj] = where
      page = @current_contents.on_page
      add_object(id) if (page.info.page_number % 2) == 1
    when :all_following_pages
      @add_loose_objects[obj] = :all_pages
    when :following_even_pages
      @add_loose_objects[obj] = :even_pages
    when :following_odd_pages
      @add_loose_objects[obj] = :odd_pages
    end
  end
end

#add_outline_item(label, title = label) ⇒ Object

Add an outline item (Bookmark).



686
687
688
# File 'lib/pdf/writer.rb', line 686

def add_outline_item(label, title = label)
  PDF::Writer::Object::Outline.new(self, label, title)
end

#add_text(x, y, text, size = nil, angle = 0, word_space_adjust = 0) ⇒ Object

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.



1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
# File 'lib/pdf/writer.rb', line 1362

def add_text(x, y, text, size = nil, angle = 0, word_space_adjust = 0)
  if text.kind_of?(Numeric) and size.kind_of?(String)
    text, size = size, text
    warn PDF::Writer::Lang[:add_text_parameters_reversed] % caller[0]
  end

  if size.nil? or size <= 0
    size = @font_size
  end

  select_font("Helvetica") if @fonts.empty?

  text = text.to_s

    # If there are any open callbacks, then they should be called, to show
    # the start of the line
  @callbacks.reverse_each do |ii|
    info = ii.dup
    info[:x]      = x
    info[:y]      = y
    info[:angle]  = angle
    info[:status] = :start_line

    info[:tag][self, info]
  end
  if angle == 0
    add_content("\nBT %.3f %.3f Td" % [x, y])
  else
    rad = PDF::Math.deg2rad(angle)
    tt = "\nBT %.3f %.3f %.3f %.3f %.3f %.3f Tm"
    tt = tt % [ Math.cos(rad), Math.sin(rad), -Math.sin(rad), Math.cos(rad), x, y ]
    add_content(tt)
  end

  if (word_space_adjust != 0) or not ((@word_space_adjust.nil?) and (@word_space_adjust != word_space_adjust))
    @word_space_adjust = word_space_adjust
    add_content(" %.3f Tw" % word_space_adjust)
  end

  pos = -1
  start = 0
  loop do
    pos += 1
    break if pos == text.size
    font_change = true
    tag_size, text, font_change = quick_text_tags(text, pos, font_change)

    if tag_size != 0
      if pos > start
        part = text[start, pos - start]
        tt = " /F#{find_font(@current_font).font_id}"
        tt << " %.1f Tf %d Tr" % [ size, @current_text_render_style ]
        tt << " (#{PDF::Writer.escape(part)}) Tj"
        add_content(tt)
      end

      if font_change
        current_font!
      else
        add_content(" ET")
        xp = x
        yp = y
        tag_size, text, font_change, xp, yp = text_tags(text, pos, font_change, true, xp, yp, size, angle, word_space_adjust)

          # Restart the text object
        if angle.zero?
          add_content("\nBT %.3f %.3f Td" % [xp, yp])
        else
          rad = PDF::Math.deg2rad(angle)
          tt = "\nBT %.3f %.3f %.3f %.3f %.3f %.3f Tm"
          tt = tt % [ Math.cos(rad), Math.sin(rad), -Math.sin(rad), Math.cos(rad), xp, yp ]
          add_content(tt)
        end

        if (word_space_adjust != 0) or (word_space_adjust != @word_space_adjust)
          @word_space_adjust = word_space_adjust
          add_content(" %.3f Tw" % [word_space_adjust])
        end
      end

      pos += tag_size - 1
      start = pos + 1
    end
  end

  if start < text.size
    part = text[start..-1]

    tt = " /F#{find_font(@current_font).font_id}"
    tt << " %.1f Tf %d Tr" % [ size, @current_text_render_style ]
    tt << " (#{PDF::Writer.escape(part)}) Tj"
    add_content(tt)
  end
  add_content(" ET")

    # XXX: Experimental fix.
  @callbacks.reverse_each do |ii|
    info = ii.dup
    info[:x]      = x
    info[:y]      = y
    info[:angle]  = angle
    info[:status] = :end_line
    info[:tag][self, info]
  end
end

#add_text_wrap(x, y, width, text, size = nil, justification = :left, angle = 0, test = false) ⇒ Object

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



1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
# File 'lib/pdf/writer.rb', line 1604

def add_text_wrap(x, y, width, text, size = nil, justification = :left, angle = 0, test = false)
  if text.kind_of?(Numeric) and size.kind_of?(String)
    text, size = size, text
    warn PDF::Writer::Lang[:add_textw_parameters_reversed] % caller[0]
  end

  if size.nil? or size <= 0
    size = @font_size
  end

    # Need to store the initial text state, as this will change during the
    # width calculation, but will need to be re-set before printing, so
    # that the chars work out right
  t_CTS = @current_text_state.dup

  select_font("Helvetica") if @fonts.empty?
  return "" if width <= 0

  w = brk = brkw = 0
  font = @current_font
  tw = width / size.to_f * 1000

  pos = -1
  loop do
    pos += 1
    break if pos == text.size
    font_change = true
    tag_size, text, font_change = quick_text_tags(text, pos, font_change)
    if tag_size != 0
      if font_change
        current_font!
        font = @current_font
      end
      pos += (tag_size - 1)
    else
      w += char_width(font, text[pos, 1])

      if w > tw # We need to truncate this line
        if brk > 0 # There is somewhere to break the line.
          if text[brk] == " "
            tmp = text[0, brk]
          else
            tmp = text[0, brk + 1]
          end
          x, adjust = adjust_wrapped_text(tmp, brkw, width, x, justification)

            # Reset the text state
          @current_text_state = t_CTS.dup
          current_font!
          add_text(x, y, tmp, size, angle, adjust) unless test
          return text[brk + 1..-1]
        else # just break before the current character
          tmp = text[0, pos]
#           tmpw = (w - char_width(font, text[pos, 1])) * size / 1000.0
          x, adjust = adjust_wrapped_text(tmp, brkw, width, x, justification)

            # Reset the text state
          @current_text_state = t_CTS.dup
          current_font!
          add_text(x, y, tmp, size, angle, adjust) unless test
          return text[pos..-1]
        end
      end

      if text[pos] == ?-
        brk = pos
        brkw = w * size / 1000.0
      end

      if text[pos, 1] == " "
        brk = pos
        ctmp = text[pos]
        ctmp = @fonts[font].differences[ctmp] unless @fonts[font].differences.nil?
        z = @fonts[font].c[tmp].nil? ? 0 : @fonts[font].c[tmp]['WX']
        brkw = (w - z) * size / 1000.0
      end
    end
  end

    # There was no need to break this line.
  justification = :left if justification == :full
  tmpw = (w * size) / 1000.0
  x, adjust = adjust_wrapped_text(text, tmpw, width, x, justification)
    # reset the text state
  @current_text_state = t_CTS.dup
  current_font!
  add_text(x, y, text, size, angle, adjust) unless test
  return ""
end

#append_pageObject

Changes the #insert_page property to append to the page set.



2016
2017
2018
# File 'lib/pdf/writer.rb', line 2016

def append_page
  insert_mode(:last)
end

#bleed_box(x0, y0, x1, y1) ⇒ Object

Sets the bleed box area.



657
658
659
# File 'lib/pdf/writer.rb', line 657

def bleed_box(x0, y0, x1, y1)
  @pages.bleed_box = [ x0, y0, x1, y1 ]
end

#check_all_hereObject

should be used for internal checks, not implemented as yet



699
700
# File 'lib/pdf/writer.rb', line 699

def check_all_here
end

#close_objectObject

Close an object for writing.



1762
1763
1764
1765
1766
1767
1768
# File 'lib/pdf/writer.rb', line 1762

def close_object
  unless @stack.empty?
    obj = @stack.pop
    @current_contents = obj[:contents]
    @current_page = obj[:page]
  end
end

#cm2pts(x) ⇒ Object

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



245
246
247
# File 'lib/pdf/writer.rb', line 245

def cm2pts(x)
  PDF::Writer.cm2pts(x)
end

#columns?Boolean

Indicates if columns are currently on.

Returns:

  • (Boolean)


1902
1903
1904
# File 'lib/pdf/writer.rb', line 1902

def columns?
  @columns_on
end

#compressed?Boolean

Returns true if the document is compressed.

Returns:

  • (Boolean)


438
439
440
# File 'lib/pdf/writer.rb', line 438

def compressed?
  @compressed == true
end

#current_font!Object

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.



997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
# File 'lib/pdf/writer.rb', line 997

def current_font!
  select_font("Helvetica") unless @current_base_font

  font = File.basename(@current_base_font)
  if @font_families[font] and @font_families[font][@current_text_state]
      # Then we are in some state or another and this font has a family,
      # and the current setting exists within it select the font, then
      # return it.
    if File.dirname(@current_base_font) != '.'
      nf = File.join(File.dirname(@current_base_font), @font_families[font][@current_text_state])
    else
      nf = @font_families[font][@current_text_state]
    end

    unless @fonts[nf]
      enc = {
        :encoding     => @fonts[font].encoding,
        :differences  => @fonts[font].differences
      }
      load_font(nf, enc)
    end
    @current_font = nf
  else
    @current_font = @current_base_font
  end
end

#current_page_numberObject

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



2111
2112
2113
# File 'lib/pdf/writer.rb', line 2111

def current_page_number
  @pageset.size
end

#font_descender(size = nil) ⇒ Object

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.



1047
1048
1049
1050
1051
1052
1053
# File 'lib/pdf/writer.rb', line 1047

def font_descender(size = nil)
  size = @font_size if size.nil? or size <= 0

  select_font("Helvetica") if @fonts.empty?
  hi = @fonts[@current_font].fontbbox[1].to_f
  (size * hi / 1000.0)
end

#font_height(size = nil) ⇒ Object

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



1035
1036
1037
1038
1039
1040
1041
# File 'lib/pdf/writer.rb', line 1035

def font_height(size = nil)
  size = @font_size if size.nil? or size <= 0

  select_font("Helvetica") if @fonts.empty?
  hh = @fonts[@current_font].fontbbox[3].to_f - @fonts[@current_font].fontbbox[1].to_f
  (size * hh / 1000.0)
end

#in2pts(x) ⇒ Object

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



257
258
259
# File 'lib/pdf/writer.rb', line 257

def in2pts(x)
  PDF::Writer.in2pts(x)
end

#insert_mode(options = {}) ⇒ Object

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)


1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
# File 'lib/pdf/writer.rb', line 1980

def insert_mode(options = {})
  case options
  when :on, true
    @insert_mode = true
  when :off, false
    @insert_mode = false
  else
    return @insert_mode unless options

    @insert_mode = options[:on] unless options[:on].nil?

    unless options[:page].nil?
      if @pageset[options[:page]].nil? or options[:page] == :last
        @insert_page = @pageset[-1]
      else
        @insert_page = @pageset[options[:page]]
      end
    end

    @insert_position = options[:position] if options[:position]
  end
end

#insert_page(page = nil) ⇒ Object

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


2007
2008
2009
2010
2011
2012
2013
2014
# File 'lib/pdf/writer.rb', line 2007

def insert_page(page = nil)
  return @insert_page unless page
  if page == :last
    @insert_page = @pageset[-1]
  else
    @insert_page = @pageset[page]
  end
end

#insert_position(position = nil) ⇒ Object

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


2025
2026
2027
2028
# File 'lib/pdf/writer.rb', line 2025

def insert_position(position = nil)
  return @insert_position unless position
  @insert_position = position
end

#lines_remaining(font_size = nil) ⇒ Object

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



2446
2447
2448
2449
2450
# File 'lib/pdf/writer.rb', line 2446

def lines_remaining(font_size = nil)
  font_size ||= @font_size
  remaining = @y - @bottom_margin
  remaining / font_height(font_size).to_f
end

#margins_cm(top, left = top, bottom = top, right = left) ⇒ Object

Define the margins in centimetres.



566
567
568
# File 'lib/pdf/writer.rb', line 566

def margins_cm(top, left = top, bottom = top, right = left)
  margins_pt(cm2pts(top), cm2pts(left), cm2pts(bottom), cm2pts(right))
end

#margins_in(top, left = top, bottom = top, right = left) ⇒ Object

Define the margins in inches.



571
572
573
# File 'lib/pdf/writer.rb', line 571

def margins_in(top, left = top, bottom = top, right = left)
  margins_pt(in2pts(top), in2pts(left), in2pts(bottom), in2pts(right))
end

#margins_mm(top, left = top, bottom = top, right = left) ⇒ Object

Define the margins in millimetres.



561
562
563
# File 'lib/pdf/writer.rb', line 561

def margins_mm(top, left = top, bottom = top, right = left)
  margins_pt(mm2pts(top), mm2pts(left), mm2pts(bottom), mm2pts(right))
end

#margins_pt(top, left = top, bottom = top, right = left) ⇒ Object

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


582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
# File 'lib/pdf/writer.rb', line 582

def margins_pt(top, left = top, bottom = top, right = left)
    # Set the margins to new values
  @top_margin    = top
  @bottom_margin = bottom
  @left_margin   = left
  @right_margin  = right
    # Check to see if this means that the current writing position is
    # outside the writable area
  if @y > (@page_height - top)
      # Move y down
    @y = @page_height - top
  end

  start_new_page if @y < bottom # Make a new page
end

#mm2pts(x) ⇒ Object

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



251
252
253
# File 'lib/pdf/writer.rb', line 251

def mm2pts(x)
  PDF::Writer.mm2pts(x)
end

#move_pointer(dy, make_space = false) ⇒ Object

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.



550
551
552
553
554
555
556
557
558
# File 'lib/pdf/writer.rb', line 550

def move_pointer(dy, make_space = false)
  @y -= dy
  if @y < @bottom_margin
    start_new_page
    @y -= dy if make_space
  elsif @y > absolute_top_margin
    @y = absolute_top_margin
  end
end

#new_page(insert = false, page = nil, pos = :after) ⇒ Object

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.



2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
# File 'lib/pdf/writer.rb', line 2084

def new_page(insert = false, page = nil, pos = :after)
  reset_state_at_page_finish

  if insert
      # The id from the PDF::Writer class is the id of the contents of the
      # page, not the page object itself. Query that object to find the
      # parent.
    _new_page = PDF::Writer::Object::Page.new(self, { :rpage => page, :pos => pos })
  else
    _new_page = PDF::Writer::Object::Page.new(self)
  end

  reset_state_at_page_start

    # If there has been a stroke or fill color set, transfer them.
  fill_color!
  stroke_color!
  stroke_style!

    # the call to the page object set @current_contents to the present page,
    # so this can be returned as the page id
#   @current_contents
  _new_page
end

#open_at(page, style, *params) ⇒ Object

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.



1829
1830
1831
1832
# File 'lib/pdf/writer.rb', line 1829

def open_at(page, style, *params)
  d = PDF::Writer::Object::Destination.new(self, page, style, *params)
  @catalog.open_here = d
end

#open_here(style, *params) ⇒ Object

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.



1821
1822
1823
# File 'lib/pdf/writer.rb', line 1821

def open_here(style, *params)
  open_at(@current_page, style, *params)
end

#open_new_objectObject

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.



2710
2711
2712
2713
2714
2715
2716
2717
# File 'lib/pdf/writer.rb', line 2710

def open_new_object
  save_state
  oid = open_object
  close_object
  add_object(oid)
  reopen_object(oid)
  oid
end

#open_object {|@current_contents| ... } ⇒ Object

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.

Yields:



1743
1744
1745
1746
1747
1748
1749
# File 'lib/pdf/writer.rb', line 1743

def open_object
  @stack << { :contents => @current_contents, :page => @current_page }
  @current_contents = PDF::Writer::Object::Contents.new(self)
  @loose_objects << @current_contents
  yield @current_contents if block_given?
  @current_contents
end

#page_mode=(mode) ⇒ Object

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.



1869
1870
1871
# File 'lib/pdf/writer.rb', line 1869

def page_mode=(mode)
  @catalog.page_mode = value
end

#prepress_center_mark(x, y, angle, mark_length = 18, bleed_size = 12) ⇒ Object

:nodoc:



2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
# File 'lib/pdf/writer.rb', line 2430

def prepress_center_mark(x, y, angle, mark_length = 18, bleed_size = 12) #:nodoc:
  save_state
  translate_axis(x, y)
  rotate_axis(angle)
  half_mark = mark_length / 2.0
  c_x = 0
  c_y = bleed_size + half_mark
  line((c_x - half_mark), c_y, (c_x + half_mark), c_y).stroke
  line(c_x, (c_y - half_mark), c_x, (c_y + half_mark)).stroke
  rad = (mark_length * 0.50) / 2.0
  circle_at(c_x, c_y, rad).stroke
  restore_state
end

#prepress_clip_mark(x, y, angle, mark_length = 18, bleed_size = 12) ⇒ Object

:nodoc:



2421
2422
2423
2424
2425
2426
2427
2428
# File 'lib/pdf/writer.rb', line 2421

def prepress_clip_mark(x, y, angle, mark_length = 18, bleed_size = 12) #:nodoc:
  save_state
  translate_axis(x, y)
  rotate_axis(angle)
  line(0, bleed_size, 0, bleed_size + mark_length).stroke
  line(bleed_size, 0, bleed_size + mark_length, 0).stroke
  restore_state
end

#render(debug = false) ⇒ Object Also known as: to_s

Return the PDF stream as a string.



703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
# File 'lib/pdf/writer.rb', line 703

def render(debug = false)
  add_page_numbers
  @compression = false if $DEBUG or debug
  @arc4.init(@encryption_key) unless @arc4.nil?

  check_all_here

  xref = []

  content = "%PDF-#{@version}\n%âãÏÓ\n"
  pos = content.size

  objects.each do |oo|
    cont = oo.to_s
    content << cont
    xref << pos
    pos += cont.size
  end

#   pos += 1 # Newline character before XREF

  content << "\nxref\n0 #{xref.size + 1}\n0000000000 65535 f \n"
  xref.each { |xx| content << "#{'%010d' % [xx]} 00000 n \n" }
  content << "\ntrailer\n"
  content << "  << /Size #{xref.size + 1}\n"
  content << "     /Root 1 0 R\n /Info #{@info.oid} 0 R\n"
    # If encryption has been applied to this document, then add the marker
    # for this dictionary
  if @arc4 and @encryption
    content << "/Encrypt #{@encryption.oid} 0 R\n"
  end

  if @file_identifier
    content << "/ID[<#{@file_identifier}><#{@file_identifier}>]\n"
  end
  content << "  >>\nstartxref\n#{pos}\n%%EOF\n"
  content
end

#reopen_object(id) ⇒ Object

Opens an existing object for editing.



1752
1753
1754
1755
1756
1757
1758
1759
# File 'lib/pdf/writer.rb', line 1752

def reopen_object(id)
  @stack << { :contents => @current_contents, :page => @current_page }
  @current_contents = id
    # if this object is the primary contents for a page, then set the
    # current page to its parent
  @current_page = @current_contents.on_page unless @current_contents.on_page.nil?
  @current_contents
end

#restore_stateObject

Restore a previously saved state.



1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
# File 'lib/pdf/writer.rb', line 1721

def restore_state
  unless @state_stack.empty?
    state = @state_stack.pop
    @current_fill_color         = state.fill_color
    @current_stroke_color       = state.stroke_color
    @current_text_render_style  = state.text_render_style
    @current_stroke_style       = state.stroke_style
    stroke_style!
  end
  add_content("\nQ")
end

#save_as(name) ⇒ Object

Save the PDF as a file to disk.



2720
2721
2722
# File 'lib/pdf/writer.rb', line 2720

def save_as(name)
  File.open(name, "wb") { |f| f.write self.render }
end

#save_stateObject

Saves the state.



1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
# File 'lib/pdf/writer.rb', line 1695

def save_state
  PDF::Writer::State.new do |state|
    state.fill_color        = @current_fill_color
    state.stroke_color      = @current_stroke_color
    state.text_render_style = @current_text_render_style
    state.stroke_style      = @current_stroke_style
    @state_stack.push state
  end
  add_content("\nq")
end

#select_font(font, encoding = nil) ⇒ Object

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



975
976
977
978
979
980
981
# File 'lib/pdf/writer.rb', line 975

def select_font(font, encoding = nil)
  load_font(font, encoding) unless @fonts[font]

  @current_base_font = font
  current_font!
  @current_base_font
end

#sizeObject

The number of PDF objects in the document



137
138
139
# File 'lib/pdf/writer.rb', line 137

def size
  @objects.size
end

#start_columns(size = 2, gutter = 10) ⇒ Object

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.



1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
# File 'lib/pdf/writer.rb', line 1910

def start_columns(size = 2, gutter = 10)
    # Start from the current y-position; make the set number of columns.
  return false if @columns_on

  @columns = {
    :current => 1,
    :bot_y   => @y
  }
  @columns_on = true
    # store the current margins
  @columns[:left]   = @left_margin
  @columns[:right]  = @right_margin
  @columns[:top]    = @top_margin
  @columns[:bottom] = @bottom_margin
    # Reset the margins to suit the new columns. Safe enough to assume the
    # first column here, but start from the current y-position.
  @top_margin = @page_height - @y
  @columns[:size]   = size   || 2
  @columns[:gutter] = gutter || 10
  w = absolute_right_margin - absolute_left_margin
  @columns[:width] = (w - ((size - 1) * gutter)) / size.to_f
  @right_margin = @page_width - (@left_margin + @columns[:width])
end

#start_new_page(force = false) ⇒ Object

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.



2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
# File 'lib/pdf/writer.rb', line 2034

def start_new_page(force = false)
  page_required = true

  if @columns_on
      # Check if this is just going to a new column. Increment the column
      # number.
    @columns[:current] += 1

    if @columns[:current] <= @columns[:size] and not force
      page_required = false
      @columns[:bot_y] = @y if @y < @columns[:bot_y]
    else
      @columns[:current] = 1
      @top_margin = @columns[:top]
      @columns[:bot_y] = absolute_top_margin
    end

    w = @columns[:width]
    g = @columns[:gutter]
    n = @columns[:current] - 1
    @left_margin = @columns[:left] + n * (g + w)
    @right_margin = @page_width - (@left_margin + w)
  end

  if page_required or force
      # make a new page, setting the writing point back to the top.
    @y = absolute_top_margin
      # make the new page with a call to the basic class
    if @insert_mode
      id = new_page(true, @insert_page, @insert_position)
      @pageset << id
        # Manipulate the insert options so that inserted pages follow each
        # other
      @insert_page = id
      @insert_position = :after
    else
      @pageset << new_page
    end

  else
    @y = absolute_top_margin
  end
  @pageset
end

#start_page_numbering(x, y, size, pos = nil, pattern = nil, starting = nil) ⇒ Object

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.



2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
# File 'lib/pdf/writer.rb', line 2129

def start_page_numbering(x, y, size, pos = nil, pattern = nil, starting = nil)   
  pos     ||= :left
  pattern ||= "<PAGENUM> of <TOTALPAGENUM>"
  starting  ||= 1

  @page_numbering ||= []
  @page_numbering << (o = {})

  page    = @pageset.size - 1
  o[page] = {
    :x        => x,
    :y        => y,
    :pos      => pos,
    :pattern  => pattern,
    :starting => starting,
    :size     => size,
    :start    => true
  }
  @page_numbering.index(o)
end

#stop_columnsObject

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.



1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
# File 'lib/pdf/writer.rb', line 1946

def stop_columns
  return false unless @columns_on
  @columns_on = false

  @columns[:bot_y] = @y if @y < @columns[:bot_y]

  if (@columns[:bot_y] > @bottom_margin) or @column_number == 1
    @y = @columns[:bot_y]
  else
    start_new_page
  end
  restore_margins_after_columns
  @columns = {}
  true
end

#stop_object(id) ⇒ Object

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



1771
1772
1773
1774
# File 'lib/pdf/writer.rb', line 1771

def stop_object(id)
  obj = @loose_objects.detect { |ii| ii.oid == id.oid }
  @add_loose_objects[obj] = nil
end

#stop_page_numbering(stop_total = false, stop_at = :current, scheme = 0) ⇒ Object

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.



2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
# File 'lib/pdf/writer.rb', line 2187

def stop_page_numbering(stop_total = false, stop_at = :current, scheme = 0)
  return false unless @page_numbering

  page = @pageset.size - 1

  @page_numbering[scheme][page] ||= {}
  o = @page_numbering[scheme][page]

  case [ stop_total, stop_at == :current ]
  when [ true, true ]
    o[:stop] = :stop_total
  when [ true, false ]
    o[:stop] = :stop_total_next
  when [ false, true ]
    o[:stop] = :stop_next
    else
    o[:stop] = :stop
  end
end

#text(text, options = {}) ⇒ Object

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.



2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
# File 'lib/pdf/writer.rb', line 2334

def text(text, options = {})
    # Apply the filtering which will make underlining (and other items)
    # function.
  text = preprocess_text(text)

  options ||= {}

  new_page_required = false
  __y = @y

  if options[:absolute_left]
    left = options[:absolute_left]
  else
    left = @left_margin
    left += options[:left] if options[:left]
  end

  if options[:absolute_right]
    right = options[:absolute_right]
  else
    right = absolute_right_margin
    right -= options[:right] if options[:right]
  end

  size = options[:font_size] || 0
  if size <= 0
    size = @font_size
  else
    @font_size = size
  end

  just = options[:justification] || :left

  if options[:leading] # leading instead of spacing
    height = options[:leading]
  elsif options[:spacing]
    height = options[:spacing] * font_height(size)
  else
    height = font_height(size)
  end

  text.each do |line|
    start = true
    loop do # while not line.empty? or start
      break if (line.nil? or line.empty?) and not start

      start = false

      @y -= height

      if @y < @bottom_margin
        if options[:test]
          new_page_required = true
        else
            # and then re-calc the left and right, in case they have
            # changed due to columns
          start_new_page
          @y -= height

          if options[:absolute_left]
            left = options[:absolute_left]
          else
            left = @left_margin
            left += options[:left] if options[:left]
          end

          if options[:absolute_right]
            right = options[:absolute_right]
          else
            right = absolute_right_margin
            right -= options[:right] if options[:right]
          end
        end
      end

      line = add_text_wrap(left, @y, right - left, line, size, just, 0, options[:test])
    end
  end

  if options[:test]
    @y = __y
    new_page_required
  else
    @y
  end
end

#text_line_width(text, size = nil) ⇒ Object

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.



1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
# File 'lib/pdf/writer.rb', line 1489

def text_line_width(text, size = nil)
  if text.kind_of?(Numeric) and size.kind_of?(String)
    text, size = size, text
    warn PDF::Writer::Lang[:text_width_parameters_reversed] % caller[0]
  end

  if size.nil? or size <= 0
    size = @font_size
  end

    # This function should not change any of the settings, though it will
    # need to track any tag which change during calculation, so copy them
    # at the start and put them back at the end.
  t_CTS = @current_text_state.dup

  select_font("Helvetica") if @fonts.empty?
    # converts a number or a float to a string so it can get the width
  tt = text.to_s
    # hmm, this is where it all starts to get tricky - use the font
    # information to calculate the width of each character, add them up
    # and convert to user units
  width = 0
  font = @current_font

  pos = -1
  loop do
    pos += 1
    break if pos == tt.size
    font_change = true
    tag_size, text, font_change = quick_text_tags(text, pos, font_change)
    if tag_size != 0
      if font_change
        current_font!
        font = @current_font
      end
      pos += tag_size - 1
    else
      if "&lt;" == tt[pos, 4]
        width += char_width(font, '<')
        pos += 3
      elsif "&gt;" == tt[pos, 4]
        width += char_width(font, '>')
        pos += 3
      elsif "&amp;" == tt[pos, 5]
        width += char_width(font, '&')
        pos += 4
      else
        width += char_width(font, tt[pos, 1])
      end
    end
  end

  @current_text_state = t_CTS.dup
  current_font!

  (width * size / 1000.0)
end

#text_width(text, size = nil) ⇒ Object

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.



1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
# File 'lib/pdf/writer.rb', line 1554

def text_width(text, size = nil)
  if text.kind_of?(Numeric) and size.kind_of?(String)
    text, size = size, text
    warn PDF::Writer::Lang[:text_width_parameters_reversed] % caller[0]
  end

  if size.nil? or size <= 0
    size = @font_size
  end

  max   = 0

  text.to_s.each do |line|
    width = text_line_width(line, size)
    max = width if width > max
  end
  max
end

#trim_box(x0, y0, x1, y1) ⇒ Object

Sets the trim box area.



652
653
654
# File 'lib/pdf/writer.rb', line 652

def trim_box(x0, y0, x1, y1)
  @pages.trim_box = [ x0, y0, x1, y1 ]
end

#viewer_preferences(label, value = 0) ⇒ Object

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



663
664
665
666
667
668
669
670
671
672
# File 'lib/pdf/writer.rb', line 663

def viewer_preferences(label, value = 0)
  @catalog.viewer_preferences ||= PDF::Writer::Object::ViewerPreferences.new(self)

    # This will only work if the label is one of the valid ones.
  if label.kind_of?(Hash)
    label.each { |kk, vv| @catalog.viewer_preferences.__send__("#{kk.downcase}=".intern, vv) }
  else
    @catalog.viewer_preferences.__send__("#{label.downcase}=".intern, value)
  end
end

#which_page_number(page_num, scheme = 0) ⇒ Object

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.



2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
# File 'lib/pdf/writer.rb', line 2157

def which_page_number(page_num, scheme = 0)
  return nil unless @page_numbering

  num   = nil
  start = start_num = 1

  @page_numbering[scheme].each do |kk, vv|
    if kk <= page_num
      if vv.kind_of?(Hash)
        unless vv[:starting].nil?
          start = vv[:starting]
          start_num = kk
          num = page_num - start_num + start
        end
      else
        num = nil
      end
    end
  end
  num
end