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$ ++

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

VERSION =

The version of PDF::Writer.

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



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
408
# File 'lib/pdf/writer.rb', line 326

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:



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

def absolute_bottom_margin
  @absolute_bottom_margin
end

#absolute_left_marginObject (readonly)

:nodoc:



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

def absolute_left_margin
  @absolute_left_margin
end

#absolute_right_marginObject (readonly)

:nodoc:



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

def absolute_right_margin
  @absolute_right_margin
end

#absolute_top_marginObject (readonly)

:nodoc:



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

def absolute_top_margin
  @absolute_top_margin
end

#absolute_x_middleObject (readonly)

:nodoc:



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

def absolute_x_middle
  @absolute_x_middle
end

#absolute_y_middleObject (readonly)

:nodoc:



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

def absolute_y_middle
  @absolute_y_middle
end

#arc4Object (readonly)

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



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

def arc4
  @arc4
end

#bottom_marginObject

Returns the value of attribute bottom_margin



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

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.



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

def catalog
  @catalog
end

#column_countObject (readonly)

:nodoc:



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

def column_count
  @column_count
end

#column_gutterObject (readonly)

:nodoc:



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

def column_gutter
  @column_gutter
end

#column_numberObject (readonly)

:nodoc:



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

def column_number
  @column_number
end

#column_widthObject (readonly)

:nodoc:



1882
1883
1884
# File 'lib/pdf/writer.rb', line 1882

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.



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

def compressed
  @compressed
end

#current_base_fontObject (readonly)

Returns the value of attribute current_base_font



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

def current_base_font
  @current_base_font
end

#current_contentsObject (readonly)

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



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

def current_contents
  @current_contents
end

#current_fontObject (readonly)

Returns the value of attribute current_font



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

def current_font
  @current_font
end

#current_pageObject

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



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

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.



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

def destinations
  @destinations
end

#encryption_keyObject

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



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

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.



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

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.



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

def font_families
  @font_families
end

#font_sizeObject

Returns the value of attribute font_size



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

def font_size
  @font_size
end

#infoObject (readonly)

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



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

def info
  @info
end

#left_marginObject

Returns the value of attribute left_margin



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

def left_margin
  @left_margin
end

#margin_heightObject (readonly)

:nodoc:



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

def margin_height
  @margin_height
end

#margin_widthObject (readonly)

:nodoc:



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

def margin_width
  @margin_width
end

#margin_x_middleObject (readonly)

:nodoc:



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

def margin_x_middle
  @margin_x_middle
end

#margin_y_middleObject (readonly)

:nodoc:



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

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.



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

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.



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

def outlines
  @outlines
end

#page_heightObject (readonly)

Returns the value of attribute page_height



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

def page_height
  @page_height
end

#page_widthObject (readonly)

Returns the value of attribute page_width



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

def page_width
  @page_width
end

#pagesObject

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



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

def pages
  @pages
end

#pagesetObject (readonly)

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



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

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.



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

def pointer
  @pointer
end

#procsetObject

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



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

def procset
  @procset
end

#right_marginObject

Returns the value of attribute right_margin



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

def right_margin
  @right_margin
end

#top_marginObject

Returns the value of attribute top_margin



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

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.



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

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.



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

def y
  @y
end

Class Method Details

.cm2pts(x) ⇒ Object

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



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

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.



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

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.



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

def in2pts(x)
  x * 72
end

.mm2pts(x) ⇒ Object

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



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

def mm2pts(x)
  (x / 25.4) * 72
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)


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
223
# File 'lib/pdf/writer.rb', line 170

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



2730
2731
2732
# File 'lib/pdf/writer.rb', line 2730

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



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

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.



1861
1862
1863
# File 'lib/pdf/writer.rb', line 1861

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.



1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
# File 'lib/pdf/writer.rb', line 1810

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)



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

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.



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

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.



1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
# File 'lib/pdf/writer.rb', line 1783

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



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

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.



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
1467
# File 'lib/pdf/writer.rb', line 1363

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



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
1693
1694
1695
1696
1697
# File 'lib/pdf/writer.rb', line 1609

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.



2021
2022
2023
# File 'lib/pdf/writer.rb', line 2021

def append_page
  insert_mode(:last)
end

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

Sets the bleed box area.



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

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



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

def check_all_here
end

#close_objectObject

Close an object for writing.



1767
1768
1769
1770
1771
1772
1773
# File 'lib/pdf/writer.rb', line 1767

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.



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

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

#columns?Boolean

Indicates if columns are currently on.

Returns:

  • (Boolean)


1907
1908
1909
# File 'lib/pdf/writer.rb', line 1907

def columns?
  @columns_on
end

#compressed?Boolean

Returns true if the document is compressed.

Returns:

  • (Boolean)


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

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.



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
1023
# File 'lib/pdf/writer.rb', line 998

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.



2116
2117
2118
# File 'lib/pdf/writer.rb', line 2116

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.



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

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.



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

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.



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

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)


1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
# File 'lib/pdf/writer.rb', line 1985

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


2012
2013
2014
2015
2016
2017
2018
2019
# File 'lib/pdf/writer.rb', line 2012

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


2030
2031
2032
2033
# File 'lib/pdf/writer.rb', line 2030

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.



2451
2452
2453
2454
2455
# File 'lib/pdf/writer.rb', line 2451

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.



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

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.



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

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.



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

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


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

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.



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

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.



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

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.



2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
# File 'lib/pdf/writer.rb', line 2089

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.



1834
1835
1836
1837
# File 'lib/pdf/writer.rb', line 1834

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.



1826
1827
1828
# File 'lib/pdf/writer.rb', line 1826

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.



2715
2716
2717
2718
2719
2720
2721
2722
# File 'lib/pdf/writer.rb', line 2715

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:



1748
1749
1750
1751
1752
1753
1754
# File 'lib/pdf/writer.rb', line 1748

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.



1874
1875
1876
# File 'lib/pdf/writer.rb', line 1874

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

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

:nodoc:



2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
# File 'lib/pdf/writer.rb', line 2435

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:



2426
2427
2428
2429
2430
2431
2432
2433
# File 'lib/pdf/writer.rb', line 2426

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.



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
741
# File 'lib/pdf/writer.rb', line 704

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%\303\242\303\243\303\217\303\223\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.



1757
1758
1759
1760
1761
1762
1763
1764
# File 'lib/pdf/writer.rb', line 1757

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.



1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
# File 'lib/pdf/writer.rb', line 1726

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.



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

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

#save_stateObject

Saves the state.



1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
# File 'lib/pdf/writer.rb', line 1700

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



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

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



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

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.



1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
# File 'lib/pdf/writer.rb', line 1915

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.



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
2078
2079
2080
2081
2082
# File 'lib/pdf/writer.rb', line 2039

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.



2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
# File 'lib/pdf/writer.rb', line 2134

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.



1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
# File 'lib/pdf/writer.rb', line 1951

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.



1776
1777
1778
1779
# File 'lib/pdf/writer.rb', line 1776

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.



2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
# File 'lib/pdf/writer.rb', line 2192

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.



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
2420
2421
2422
2423
2424
# File 'lib/pdf/writer.rb', line 2339

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



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
1546
1547
1548
1549
1550
# File 'lib/pdf/writer.rb', line 1494

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.



1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
# File 'lib/pdf/writer.rb', line 1559

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



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

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.



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

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.



2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
# File 'lib/pdf/writer.rb', line 2162

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