To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Tag: | Revision:

root / .svn / pristine / 7d / 7d991b7e62a4f89e1face67a97364500bbbfb59d.svn-base @ 1297:0a574315af3e

History | View | Annotate | Download (60.9 KB)

1
# Ruby FPDF 1.53d
2
# FPDF 1.53 by Olivier Plathey ported to Ruby by Brian Ollenberger
3
# Copyright 2005 Brian Ollenberger
4
# Please retain this entire copyright notice. If you distribute any
5
# modifications, place an additional comment here that clearly indicates
6
# that it was modified. You may (but are not  send any useful modifications that you make
7
# back to me at http://zeropluszero.com/software/fpdf/
8

    
9
# Bug fixes, examples, external fonts, JPEG support, and upgrade to version
10
# 1.53 contributed by Kim Shrier.
11
#
12
# Bookmark support contributed by Sylvain Lafleur.
13
#
14
# EPS support contributed by Thiago Jackiw, ported from the PHP version by Valentin Schmidt.
15
#
16
# Bookmarks contributed by Sylvain Lafleur.
17
#
18
# 1.53 contributed by Ed Moss
19
#   Make sure all \n references are inside double quotes - Fix some multicell bugs
20
#   Handle "\n" at the beginning of a string
21
# Bookmarks contributed by Sylvain Lafleur.
22

    
23
require 'date'
24
require 'zlib'
25

    
26
class FPDF
27
  include RFPDF
28
  
29
  attr_accessor :default_font
30
	
31
    FPDF_VERSION = '1.53d'
32

    
33
    Charwidths =  {
34
        'courier'=>[600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600],
35
        
36
        'courierB'=>[600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600],
37
        
38
        'courierI'=>[600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600],
39
        
40
        'courierBI'=>[600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600],
41
        
42
        'helvetica'=>[278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 355, 556, 556, 889, 667, 191, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 278, 278, 584, 584, 584, 556, 1015, 667, 667, 722, 722, 667, 611, 778, 722, 278, 500, 667, 556, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 278, 278, 278, 469, 556, 333, 556, 556, 500, 556, 556, 278, 556, 556, 222, 222, 500, 222, 833, 556, 556, 556, 556, 333, 500, 278, 556, 500, 722, 500, 500, 500, 334, 260, 334, 584, 350, 556, 350, 222, 556, 333, 1000, 556, 556, 333, 1000, 667, 333, 1000, 350, 611, 350, 350, 222, 222, 333, 333, 350, 556, 1000, 333, 1000, 500, 333, 944, 350, 500, 667, 278, 333, 556, 556, 556, 556, 260, 556, 333, 737, 370, 556, 584, 333, 737, 333, 400, 584, 333, 333, 333, 556, 537, 278, 333, 333, 365, 556, 834, 834, 834, 611, 667, 667, 667, 667, 667, 667, 1000, 722, 667, 667, 667, 667, 278, 278, 278, 278, 722, 722, 778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 667, 667, 611, 556, 556, 556, 556, 556, 556, 889, 500, 556, 556, 556, 556, 278, 278, 278, 278, 556, 556, 556, 556, 556, 556, 556, 584, 611, 556, 556, 556, 556, 500, 556, 500],
43
        
44
        'helveticaB'=>[278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 333, 474, 556, 556, 889, 722, 238, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 333, 333, 584, 584, 584, 611, 975, 722, 722, 722, 722, 667, 611, 778, 722, 278, 556, 722, 611, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 333, 278, 333, 584, 556, 333, 556, 611, 556, 611, 556, 333, 611, 611, 278, 278, 556, 278, 889, 611, 611, 611, 611, 389, 556, 333, 611, 556, 778, 556, 556, 500, 389, 280, 389, 584, 350, 556, 350, 278, 556, 500, 1000, 556, 556, 333, 1000, 667, 333, 1000, 350, 611, 350, 350, 278, 278, 500, 500, 350, 556, 1000, 333, 1000, 556, 333, 944, 350, 500, 667, 278, 333, 556, 556, 556, 556, 280, 556, 333, 737, 370, 556, 584, 333, 737, 333, 400, 584, 333, 333, 333, 611, 556, 278, 333, 333, 365, 556, 834, 834, 834, 611, 722, 722, 722, 722, 722, 722, 1000, 722, 667, 667, 667, 667, 278, 278, 278, 278, 722, 722, 778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 667, 667, 611, 556, 556, 556, 556, 556, 556, 889, 556, 556, 556, 556, 556, 278, 278, 278, 278, 611, 611, 611, 611, 611, 611, 611, 584, 611, 611, 611, 611, 611, 556, 611, 556],
45
        
46
        'helveticaI'=>[278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 355, 556, 556, 889, 667, 191, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 278, 278, 584, 584, 584, 556, 1015, 667, 667, 722, 722, 667, 611, 778, 722, 278, 500, 667, 556, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 278, 278, 278, 469, 556, 333, 556, 556, 500, 556, 556, 278, 556, 556, 222, 222, 500, 222, 833, 556, 556, 556, 556, 333, 500, 278, 556, 500, 722, 500, 500, 500, 334, 260, 334, 584, 350, 556, 350, 222, 556, 333, 1000, 556, 556, 333, 1000, 667, 333, 1000, 350, 611, 350, 350, 222, 222, 333, 333, 350, 556, 1000, 333, 1000, 500, 333, 944, 350, 500, 667, 278, 333, 556, 556, 556, 556, 260, 556, 333, 737, 370, 556, 584, 333, 737, 333, 400, 584, 333, 333, 333, 556, 537, 278, 333, 333, 365, 556, 834, 834, 834, 611, 667, 667, 667, 667, 667, 667, 1000, 722, 667, 667, 667, 667, 278, 278, 278, 278, 722, 722, 778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 667, 667, 611, 556, 556, 556, 556, 556, 556, 889, 500, 556, 556, 556, 556, 278, 278, 278, 278, 556, 556, 556, 556, 556, 556, 556, 584, 611, 556, 556, 556, 556, 500, 556, 500],
47
        
48
        'helveticaBI'=>[278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 333, 474, 556, 556, 889, 722, 238, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 333, 333, 584, 584, 584, 611, 975, 722, 722, 722, 722, 667, 611, 778, 722, 278, 556, 722, 611, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 333, 278, 333, 584, 556, 333, 556, 611, 556, 611, 556, 333, 611, 611, 278, 278, 556, 278, 889, 611, 611, 611, 611, 389, 556, 333, 611, 556, 778, 556, 556, 500, 389, 280, 389, 584, 350, 556, 350, 278, 556, 500, 1000, 556, 556, 333, 1000, 667, 333, 1000, 350, 611, 350, 350, 278, 278, 500, 500, 350, 556, 1000, 333, 1000, 556, 333, 944, 350, 500, 667, 278, 333, 556, 556, 556, 556, 280, 556, 333, 737, 370, 556, 584, 333, 737, 333, 400, 584, 333, 333, 333, 611, 556, 278, 333, 333, 365, 556, 834, 834, 834, 611, 722, 722, 722, 722, 722, 722, 1000, 722, 667, 667, 667, 667, 278, 278, 278, 278, 722, 722, 778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 667, 667, 611, 556, 556, 556, 556, 556, 556, 889, 556, 556, 556, 556, 556, 278, 278, 278, 278, 611, 611, 611, 611, 611, 611, 611, 584, 611, 611, 611, 611, 611, 556, 611, 556],
49
        
50
        'times'=>[250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 333, 408, 500, 500, 833, 778, 180, 333, 333, 500, 564, 250, 333, 250, 278, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 278, 278, 564, 564, 564, 444, 921, 722, 667, 667, 722, 611, 556, 722, 722, 333, 389, 722, 611, 889, 722, 722, 556, 722, 667, 556, 611, 722, 722, 944, 722, 722, 611, 333, 278, 333, 469, 500, 333, 444, 500, 444, 500, 444, 333, 500, 500, 278, 278, 500, 278, 778, 500, 500, 500, 500, 333, 389, 278, 500, 500, 722, 500, 500, 444, 480, 200, 480, 541, 350, 500, 350, 333, 500, 444, 1000, 500, 500, 333, 1000, 556, 333, 889, 350, 611, 350, 350, 333, 333, 444, 444, 350, 500, 1000, 333, 980, 389, 333, 722, 350, 444, 722, 250, 333, 500, 500, 500, 500, 200, 500, 333, 760, 276, 500, 564, 333, 760, 333, 400, 564, 300, 300, 333, 500, 453, 250, 333, 300, 310, 500, 750, 750, 750, 444, 722, 722, 722, 722, 722, 722, 889, 667, 611, 611, 611, 611, 333, 333, 333, 333, 722, 722, 722, 722, 722, 722, 722, 564, 722, 722, 722, 722, 722, 722, 556, 500, 444, 444, 444, 444, 444, 444, 667, 444, 444, 444, 444, 444, 278, 278, 278, 278, 500, 500, 500, 500, 500, 500, 500, 564, 500, 500, 500, 500, 500, 500, 500, 500],
51
        
52
        'timesB'=>[250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 333, 555, 500, 500, 1000, 833, 278, 333, 333, 500, 570, 250, 333, 250, 278, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 333, 333, 570, 570, 570, 500, 930, 722, 667, 722, 722, 667, 611, 778, 778, 389, 500, 778, 667, 944, 722, 778, 611, 778, 722, 556, 667, 722, 722, 1000, 722, 722, 667, 333, 278, 333, 581, 500, 333, 500, 556, 444, 556, 444, 333, 500, 556, 278, 333, 556, 278, 833, 556, 500, 556, 556, 444, 389, 333, 556, 500, 722, 500, 500, 444, 394, 220, 394, 520, 350, 500, 350, 333, 500, 500, 1000, 500, 500, 333, 1000, 556, 333, 1000, 350, 667, 350, 350, 333, 333, 500, 500, 350, 500, 1000, 333, 1000, 389, 333, 722, 350, 444, 722, 250, 333, 500, 500, 500, 500, 220, 500, 333, 747, 300, 500, 570, 333, 747, 333, 400, 570, 300, 300, 333, 556, 540, 250, 333, 300, 330, 500, 750, 750, 750, 500, 722, 722, 722, 722, 722, 722, 1000, 722, 667, 667, 667, 667, 389, 389, 389, 389, 722, 722, 778, 778, 778, 778, 778, 570, 778, 722, 722, 722, 722, 722, 611, 556, 500, 500, 500, 500, 500, 500, 722, 444, 444, 444, 444, 444, 278, 278, 278, 278, 500, 556, 500, 500, 500, 500, 500, 570, 500, 556, 556, 556, 556, 500, 556, 500],
53
        
54
        'timesI'=>[250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 333, 420, 500, 500, 833, 778, 214, 333, 333, 500, 675, 250, 333, 250, 278, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 333, 333, 675, 675, 675, 500, 920, 611, 611, 667, 722, 611, 611, 722, 722, 333, 444, 667, 556, 833, 667, 722, 611, 722, 611, 500, 556, 722, 611, 833, 611, 556, 556, 389, 278, 389, 422, 500, 333, 500, 500, 444, 500, 444, 278, 500, 500, 278, 278, 444, 278, 722, 500, 500, 500, 500, 389, 389, 278, 500, 444, 667, 444, 444, 389, 400, 275, 400, 541, 350, 500, 350, 333, 500, 556, 889, 500, 500, 333, 1000, 500, 333, 944, 350, 556, 350, 350, 333, 333, 556, 556, 350, 500, 889, 333, 980, 389, 333, 667, 350, 389, 556, 250, 389, 500, 500, 500, 500, 275, 500, 333, 760, 276, 500, 675, 333, 760, 333, 400, 675, 300, 300, 333, 500, 523, 250, 333, 300, 310, 500, 750, 750, 750, 500, 611, 611, 611, 611, 611, 611, 889, 667, 611, 611, 611, 611, 333, 333, 333, 333, 722, 667, 722, 722, 722, 722, 722, 675, 722, 722, 722, 722, 722, 556, 611, 500, 500, 500, 500, 500, 500, 500, 667, 444, 444, 444, 444, 444, 278, 278, 278, 278, 500, 500, 500, 500, 500, 500, 500, 675, 500, 500, 500, 500, 500, 444, 500, 444],
55
        
56
        'timesBI'=>[250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 389, 555, 500, 500, 833, 778, 278, 333, 333, 500, 570, 250, 333, 250, 278, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 333, 333, 570, 570, 570, 500, 832, 667, 667, 667, 722, 667, 667, 722, 778, 389, 500, 667, 611, 889, 722, 722, 611, 722, 667, 556, 611, 722, 667, 889, 667, 611, 611, 333, 278, 333, 570, 500, 333, 500, 500, 444, 500, 444, 333, 500, 556, 278, 278, 500, 278, 778, 556, 500, 500, 500, 389, 389, 278, 556, 444, 667, 500, 444, 389, 348, 220, 348, 570, 350, 500, 350, 333, 500, 500, 1000, 500, 500, 333, 1000, 556, 333, 944, 350, 611, 350, 350, 333, 333, 500, 500, 350, 500, 1000, 333, 1000, 389, 333, 722, 350, 389, 611, 250, 389, 500, 500, 500, 500, 220, 500, 333, 747, 266, 500, 606, 333, 747, 333, 400, 570, 300, 300, 333, 576, 500, 250, 333, 300, 300, 500, 750, 750, 750, 500, 667, 667, 667, 667, 667, 667, 944, 667, 667, 667, 667, 667, 389, 389, 389, 389, 722, 722, 722, 722, 722, 722, 722, 570, 722, 722, 722, 722, 722, 611, 611, 500, 500, 500, 500, 500, 500, 500, 722, 444, 444, 444, 444, 444, 278, 278, 278, 278, 500, 556, 500, 500, 500, 500, 500, 570, 500, 556, 556, 556, 556, 444, 500, 444],
57
        
58
        'symbol'=>[250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 333, 713, 500, 549, 833, 778, 439, 333, 333, 500, 549, 250, 549, 250, 278, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 278, 278, 549, 549, 549, 444, 549, 722, 667, 722, 612, 611, 763, 603, 722, 333, 631, 722, 686, 889, 722, 722, 768, 741, 556, 592, 611, 690, 439, 768, 645, 795, 611, 333, 863, 333, 658, 500, 500, 631, 549, 549, 494, 439, 521, 411, 603, 329, 603, 549, 549, 576, 521, 549, 549, 521, 549, 603, 439, 576, 713, 686, 493, 686, 494, 480, 200, 480, 549, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 750, 620, 247, 549, 167, 713, 500, 753, 753, 753, 753, 1042, 987, 603, 987, 603, 400, 549, 411, 549, 549, 713, 494, 460, 549, 549, 549, 549, 1000, 603, 1000, 658, 823, 686, 795, 987, 768, 768, 823, 768, 768, 713, 713, 713, 713, 713, 713, 713, 768, 713, 790, 790, 890, 823, 549, 250, 713, 603, 603, 1042, 987, 603, 987, 603, 494, 329, 790, 790, 786, 713, 384, 384, 384, 384, 384, 384, 494, 494, 494, 494, 0, 329, 274, 686, 686, 686, 384, 384, 384, 384, 384, 384, 494, 494, 494, 0],
59
        
60
        'zapfdingbats'=>[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 278, 974, 961, 974, 980, 719, 789, 790, 791, 690, 960, 939, 549, 855, 911, 933, 911, 945, 974, 755, 846, 762, 761, 571, 677, 763, 760, 759, 754, 494, 552, 537, 577, 692, 786, 788, 788, 790, 793, 794, 816, 823, 789, 841, 823, 833, 816, 831, 923, 744, 723, 749, 790, 792, 695, 776, 768, 792, 759, 707, 708, 682, 701, 826, 815, 789, 789, 707, 687, 696, 689, 786, 787, 713, 791, 785, 791, 873, 761, 762, 762, 759, 759, 892, 892, 788, 784, 438, 138, 277, 415, 392, 392, 668, 668, 0, 390, 390, 317, 317, 276, 276, 509, 509, 410, 410, 234, 234, 334, 334, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 732, 544, 544, 910, 667, 760, 760, 776, 595, 694, 626, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 894, 838, 1016, 458, 748, 924, 748, 918, 927, 928, 928, 834, 873, 828, 924, 924, 917, 930, 931, 463, 883, 836, 836, 867, 867, 696, 696, 874, 0, 874, 760, 946, 771, 865, 771, 888, 967, 888, 831, 873, 927, 970, 918, 0]
61
    }
62

    
63
    def initialize(orientation='P', unit='mm', format='A4')
64
        # Initialization of properties
65
        @page=0
66
        @n=2
67
        @buffer=''
68
        @pages=[]
69
        @OrientationChanges=[]
70
        @state=0
71
        @default_font = "arial"
72
        @fonts={}
73
        @FontFiles={}
74
        @diffs=[]
75
        @images={}
76
        @links=[]
77
        @PageLinks={}
78
        @InFooter=false
79
        @FontFamily=''
80
        @FontStyle=''
81
        @FontSizePt=12
82
        @underline= false
83
        @DrawColor='0 G'
84
        @FillColor='0 g'
85
        @TextColor='0 g'
86
        @ColorFlag=false
87
        @ws=0
88
        @offsets=[]
89

    
90
        # Standard fonts
91
        @CoreFonts={}
92
        @CoreFonts['courier']='Courier'
93
        @CoreFonts['courierB']='Courier-Bold'
94
        @CoreFonts['courierI']='Courier-Oblique'
95
        @CoreFonts['courierBI']='Courier-BoldOblique'
96
        @CoreFonts['helvetica']='Helvetica'
97
        @CoreFonts['helveticaB']='Helvetica-Bold'
98
        @CoreFonts['helveticaI']='Helvetica-Oblique'
99
        @CoreFonts['helveticaBI']='Helvetica-BoldOblique'
100
        @CoreFonts['times']='Times-Roman'
101
        @CoreFonts['timesB']='Times-Bold'
102
        @CoreFonts['timesI']='Times-Italic'
103
        @CoreFonts['timesBI']='Times-BoldItalic'
104
        @CoreFonts['symbol']='Symbol'
105
        @CoreFonts['zapfdingbats']='ZapfDingbats'
106

    
107
        # Scale factor
108
        if unit=='pt'
109
            @k=1
110
        elsif unit=='mm'
111
            @k=72/25.4
112
        elsif unit=='cm'
113
            @k=72/2.54;
114
        elsif unit=='in'
115
            @k=72
116
        else
117
            raise 'Incorrect unit: '+unit
118
        end
119

    
120
        # Page format
121
        if format.is_a? String
122
            format.downcase!
123
            if format=='a3'
124
                format=[841.89,1190.55]
125
            elsif format=='a4'
126
                format=[595.28,841.89]
127
            elsif format=='a5'
128
                format=[420.94,595.28]
129
            elsif format=='letter'
130
                format=[612,792]
131
            elsif format=='legal'
132
                format=[612,1008]
133
            else
134
                raise 'Unknown page format: '+format
135
            end
136
            @fwPt,@fhPt=format
137
        else
138
            @fwPt=format[0]*@k
139
            @fhPt=format[1]*@k
140
        end
141
        @fw=@fwPt/@k;
142
        @fh=@fhPt/@k;
143

    
144
        # Page orientation
145
        orientation.downcase!
146
        if orientation=='p' or orientation=='portrait'
147
            @DefOrientation='P'
148
            @wPt=@fwPt
149
            @hPt=@fhPt
150
        elsif orientation=='l' or orientation=='landscape'
151
            @DefOrientation='L'
152
            @wPt=@fhPt
153
            @hPt=@fwPt
154
        else
155
            raise 'Incorrect orientation: '+orientation
156
        end
157
        @CurOrientation=@DefOrientation
158
        @w=@wPt/@k
159
        @h=@hPt/@k
160

    
161
        # Page margins (1 cm)
162
        margin=28.35/@k
163
        SetMargins(margin,margin)
164
        # Interior cell margin (1 mm)
165
        @cMargin=margin/10
166
        # Line width (0.2 mm)
167
        @LineWidth=0.567/@k
168
        # Automatic page break
169
        SetAutoPageBreak(true,2*margin)
170
        # Full width display mode
171
        SetDisplayMode('fullwidth')
172
        # Enable compression
173
        SetCompression(true)
174
        # Set default PDF version number
175
        @PDFVersion='1.3'
176
    end
177

    
178
    def GetMargins()
179
        return @lMargin, @tMargin, @rMargin
180
    end
181

    
182
    def SetMargins(left, top, right=-1)
183
        # Set left, top and right margins
184
        @lMargin=left
185
        @tMargin=top
186
        right=left if right==-1
187
        @rMargin=right
188
    end
189

    
190
    def SetLeftMargin(margin)
191
        # Set left margin
192
        @lMargin=margin
193
        @x=margin if @page>0 and @x<margin
194
    end
195

    
196
    def SetTopMargin(margin)
197
        # Set top margin
198
        @tMargin=margin
199
    end
200

    
201
    def SetRightMargin(margin)
202
        #Set right margin
203
        @rMargin=margin
204
    end
205

    
206
    def SetAutoPageBreak(auto, margin=0)
207
        # Set auto page break mode and triggering margin
208
        @AutoPageBreak=auto
209
        @bMargin=margin
210
        @PageBreakTrigger=@h-margin
211
    end
212

    
213
    def SetDisplayMode(zoom, layout='continuous')
214
        # Set display mode in viewer
215
        if zoom=='fullpage' or zoom=='fullwidth' or zoom=='real' or
216
            zoom=='default' or not zoom.kind_of? String
217

    
218
            @ZoomMode=zoom;
219
        elsif zoom=='zoom'
220
            @ZoomMode=layout
221
        else
222
            raise 'Incorrect zoom display mode: '+zoom
223
        end
224
        if layout=='single' or layout=='continuous' or layout=='two' or
225
            layout=='default'
226

    
227
            @LayoutMode=layout
228
        elsif zoom!='zoom'
229
            raise 'Incorrect layout display mode: '+layout
230
        end
231
    end
232

    
233
    def SetCompression(compress)
234
        # Set page compression
235
        @compress = compress
236
    end
237

    
238
    def SetTitle(title)
239
        # Title of document
240
        @title=title
241
    end
242

    
243
    def SetSubject(subject)
244
        # Subject of document
245
        @subject=subject
246
    end
247

    
248
    def SetAuthor(author)
249
        # Author of document
250
        @author=author
251
    end
252

    
253
    def SetKeywords(keywords)
254
        # Keywords of document
255
        @keywords=keywords
256
    end
257

    
258
    def SetCreator(creator)
259
        # Creator of document
260
        @creator=creator
261
    end
262

    
263
    def AliasNbPages(aliasnb='{nb}')
264
        # Define an alias for total number of pages
265
        @AliasNbPages=aliasnb
266
    end
267
    
268
    def Error(msg)
269
        raise 'FPDF error: '+msg
270
    end
271

    
272
    def Open
273
        # Begin document
274
        @state=1
275
    end
276

    
277
    def Close
278
        # Terminate document
279
        return if @state==3
280
        self.AddPage if @page==0
281
        # Page footer
282
        @InFooter=true
283
        self.Footer
284
        @InFooter=false
285
        # Close page
286
        endpage
287
        # Close document
288
        enddoc
289
    end
290

    
291
    def AddPage(orientation='')
292
        # Start a new page
293
        self.Open if @state==0
294
        family=@FontFamily
295
        style=@FontStyle+(@underline ? 'U' : '')
296
        size=@FontSizePt
297
        lw=@LineWidth
298
        dc=@DrawColor
299
        fc=@FillColor
300
        tc=@TextColor
301
        cf=@ColorFlag
302
        if @page>0
303
            # Page footer
304
            @InFooter=true
305
            self.Footer
306
            @InFooter=false
307
            # Close page
308
            endpage
309
        end
310
        # Start new page
311
        beginpage(orientation)
312
        # Set line cap style to square
313
        out('2 J')
314
        # Set line width
315
        @LineWidth=lw
316
        out(sprintf('%.2f w',lw*@k))
317
        # Set font
318
        SetFont(family,style,size) if family
319
        # Set colors
320
        @DrawColor=dc
321
        out(dc) if dc!='0 G'
322
        @FillColor=fc
323
        out(fc) if fc!='0 g'
324
        @TextColor=tc
325
        @ColorFlag=cf
326
        # Page header
327
        self.Header
328
        # Restore line width
329
        if @LineWidth!=lw
330
            @LineWidth=lw
331
            out(sprintf('%.2f w',lw*@k))
332
        end
333
        # Restore font
334
        self.SetFont(family,style,size) if family
335
        # Restore colors
336
        if @DrawColor!=dc
337
            @DrawColor=dc
338
            out(dc)
339
        end
340
        if @FillColor!=fc
341
            @FillColor=fc
342
            out(fc)
343
        end
344
        @TextColor=tc
345
        @ColorFlag=cf
346
    end
347
	  alias_method :add_page, :AddPage
348

    
349
    def Header
350
        # To be implemented in your inherited class
351
    end
352

    
353
    def Footer
354
        # To be implemented in your inherited class
355
    end
356

    
357
    def PageNo
358
        # Get current page number
359
        @page
360
    end
361

    
362
    def SetDrawColor(r,g=-1,b=-1)
363
        # Set color for all stroking operations
364
        if (r==0 and g==0 and b==0) or g==-1
365
            @DrawColor=sprintf('%.3f G',r/255.0)
366
        else
367
            @DrawColor=sprintf('%.3f %.3f %.3f RG',r/255.0,g/255.0,b/255.0)
368
        end
369
        out(@DrawColor) if(@page>0)
370
    end
371

    
372
    def SetFillColor(r,g=-1,b=-1)
373
        # Set color for all filling operations
374
        if (r==0 and g==0 and b==0) or g==-1
375
            @FillColor=sprintf('%.3f g',r/255.0)
376
        else
377
            @FillColor=sprintf('%.3f %.3f %.3f rg',r/255.0,g/255.0,b/255.0)
378
        end
379
        @ColorFlag=(@FillColor!=@TextColor)
380
        out(@FillColor) if(@page>0)
381
    end
382

    
383
    def SetTextColor(r,g=-1,b=-1)
384
        # Set color for text
385
        if (r==0 and g==0 and b==0) or g==-1
386
            @TextColor=sprintf('%.3f g',r/255.0)
387
        else
388
            @TextColor=sprintf('%.3f %.3f %.3f rg',r/255.0,g/255.0,b/255.0)
389
        end
390
        @ColorFlag=(@FillColor!=@TextColor)
391
    end
392
    
393
    def GetCharWidth(widths, index)
394
      if index.is_a?(String)
395
        widths[index.ord]
396
      else
397
        widths[index]
398
      end
399
    end
400

    
401
    def GetStringWidth(s)
402
        # Get width of a string in the current font
403
        cw=@CurrentFont['cw']
404
        w=0
405
        s.each_byte do |c|
406
            w=w+GetCharWidth(cw, c)
407
        end
408
        w*@FontSize/1000.0
409
    end
410

    
411
    def SetLineWidth(width)
412
        # Set line width
413
        @LineWidth=width
414
        out(sprintf('%.2f w',width*@k)) if @page>0
415
    end
416

    
417
    def Circle(mid_x, mid_y, radius, style='')
418
      mid_y = (@h-mid_y)*@k
419
      out(sprintf("q\n")) # postscript content in pdf
420
      # init line type etc. with /GSD gs G g (grey) RG rg (RGB) w=line witdh etc. 
421
      out(sprintf("1 j\n")) # line join
422
      # translate ("move") circle to mid_y, mid_y
423
      out(sprintf("1 0 0 1 %f %f cm", mid_x, mid_y))
424
      kappa = 0.5522847498307933984022516322796
425
      # Quadrant 1 
426
      x_s = 0.0 # 12 o'clock 
427
      y_s = 0.0 + radius
428
      x_e = 0.0 + radius # 3 o'clock 
429
      y_e = 0.0
430
      out(sprintf("%f %f m\n", x_s, y_s)) # move to 12 o'clock 
431
      # cubic bezier control point 1, start height and kappa * radius to the right 
432
      bx_e1 = x_s + (radius * kappa)
433
      by_e1 = y_s
434
      # cubic bezier control point 2, end and kappa * radius above 
435
      bx_e2 = x_e
436
      by_e2 = y_e + (radius * kappa)
437
      # draw cubic bezier from current point to x_e/y_e with bx_e1/by_e1 and bx_e2/by_e2 as bezier control points
438
      out(sprintf("%f %f %f %f %f %f c\n", bx_e1, by_e1, bx_e2, by_e2, x_e, y_e))
439
      # Quadrant 2 
440
      x_s = x_e 
441
      y_s = y_e # 3 o'clock 
442
      x_e = 0.0 
443
      y_e = 0.0 - radius # 6 o'clock 
444
      bx_e1 = x_s # cubic bezier point 1 
445
      by_e1 = y_s - (radius * kappa)
446
      bx_e2 = x_e + (radius * kappa) # cubic bezier point 2 
447
      by_e2 = y_e
448
      out(sprintf("%f %f %f %f %f %f c\n", bx_e1, by_e1, bx_e2, by_e2, x_e, y_e))
449
      # Quadrant 3 
450
      x_s = x_e 
451
      y_s = y_e # 6 o'clock 
452
      x_e = 0.0 - radius
453
      y_e = 0.0 # 9 o'clock 
454
      bx_e1 = x_s - (radius * kappa) # cubic bezier point 1 
455
      by_e1 = y_s
456
      bx_e2 = x_e # cubic bezier point 2 
457
      by_e2 = y_e - (radius * kappa)
458
      out(sprintf("%f %f %f %f %f %f c\n", bx_e1, by_e1, bx_e2, by_e2, x_e, y_e))
459
      # Quadrant 4 
460
      x_s = x_e 
461
      y_s = y_e # 9 o'clock 
462
      x_e = 0.0 
463
      y_e = 0.0 + radius # 12 o'clock 
464
      bx_e1 = x_s # cubic bezier point 1 
465
      by_e1 = y_s + (radius * kappa)
466
      bx_e2 = x_e - (radius * kappa) # cubic bezier point 2 
467
      by_e2 = y_e
468
      out(sprintf("%f %f %f %f %f %f c\n", bx_e1, by_e1, bx_e2, by_e2, x_e, y_e))
469
      if style=='F'
470
          op='f'
471
      elsif style=='FD' or style=='DF'
472
          op='b'
473
      else
474
          op='s'
475
      end
476
      out(sprintf("#{op}\n")) # stroke circle, do not fill and close path 
477
      # for filling etc. b, b*, f, f*
478
      out(sprintf("Q\n")) # finish postscript in PDF
479
    end
480
    
481
    def Line(x1, y1, x2, y2)
482
        # Draw a line
483
        out(sprintf('%.2f %.2f m %.2f %.2f l S',
484
            x1*@k,(@h-y1)*@k,x2*@k,(@h-y2)*@k))
485
    end
486

    
487
    def Rect(x, y, w, h, style='')
488
        # Draw a rectangle
489
        if style=='F'
490
            op='f'
491
        elsif style=='FD' or style=='DF'
492
            op='B'
493
        else
494
            op='S'
495
        end
496
        # x y width height re
497
        out(sprintf('%.2f %.2f %.2f %.2f re %s', x*@k,(@h-y)*@k,w*@k,-h*@k,op))
498
    end
499

    
500
    def AddFont(family, style='', file='')
501
         # Add a TrueType or Type1 font
502
         family = family.downcase
503
         family = 'helvetica' if family == 'arial'
504

    
505
         style = style.upcase
506
         style = 'BI' if style == 'IB'
507

    
508
        fontkey = family + style
509

    
510
        if @fonts.has_key?(fontkey)
511
             self.Error("Font already added: #{family} #{style}")
512
        end
513

    
514
        file = family.gsub(' ', '') + style.downcase + '.rb' if file == ''
515

    
516
        if self.class.const_defined? 'FPDF_FONTPATH'
517
            if FPDF_FONTPATH[-1,1] == '/'
518
                file = FPDF_FONTPATH + file
519
            else
520
                file = FPDF_FONTPATH + '/' + file
521
            end
522
        end
523
        
524
        # Changed from "require file" to fix bug reported by Hans Allis.
525
        load file
526

    
527
        if FontDef.desc.nil?
528
           self.Error("Could not include font definition file #{file}")
529
        end
530

    
531
        i = @fonts.length + 1
532

    
533
        @fonts[fontkey] = {'i'   => i,
534
                          'type' => FontDef.type,
535
                          'name' => FontDef.name,
536
                          'desc' => FontDef.desc,
537
                            'up' => FontDef.up,
538
                            'ut' => FontDef.ut,
539
                            'cw' => FontDef.cw,
540
                           'enc' => FontDef.enc,
541
                          'file' => FontDef.file
542
                       }
543

    
544
        if FontDef.diff
545
            # Search existing encodings
546
            unless @diffs.include?(FontDef.diff)
547
                @diffs.push(FontDef.diff)
548
                @fonts[fontkey]['diff'] = @diffs.length - 1
549
            end
550
        end
551

    
552
        if FontDef.file
553
             if FontDef.type == 'TrueType'
554
                 @FontFiles[FontDef.file] = {'length1' => FontDef.originalsize}
555
             else
556
                 @FontFiles[FontDef.file] = {'length1' => FontDef.size1, 'length2' => FontDef.size2}
557
            end
558
        end
559

    
560
        return self
561
    end
562

    
563
    def SetFont(family, style='', size=0)
564
        # Select a font; size given in points
565
        family.downcase!
566
        family=@FontFamily if family==''
567
        if family=='arial'
568
            family='helvetica'
569
        elsif family=='symbol' or family=='zapfdingbats'
570
            style=''
571
        end
572
        style.upcase!
573
        unless style.index('U').nil?
574
            @underline=true
575
            style.gsub!('U','')
576
        else
577
            @underline=false;
578
        end
579
        style='BI' if style=='IB'
580
        size=@FontSizePt if size==0
581
        # Test if font is already selected
582
        return if @FontFamily==family and
583
            @FontStyle==style and @FontSizePt==size
584
        # Test if used for the first time
585
        fontkey=family+style
586
        unless @fonts.has_key?(fontkey)
587
            if @CoreFonts.has_key?(fontkey)
588
                unless Charwidths.has_key?(fontkey)
589
                    raise 'Font unavailable'
590
                end
591
                @fonts[fontkey]={
592
                    'i'=>@fonts.size,
593
                    'type'=>'core',
594
                    'name'=>@CoreFonts[fontkey],
595
                    'up'=>-100,
596
                    'ut'=>50,
597
                    'cw'=>Charwidths[fontkey]}
598
            else
599
                raise 'Font unavailable'
600
            end
601
        end
602

    
603
        #Select it
604
        @FontFamily=family
605
        @FontStyle=style;
606
        @FontSizePt=size
607
        @FontSize=size/@k;
608
        @CurrentFont=@fonts[fontkey]
609
        if @page>0
610
            out(sprintf('BT /F%d %.2f Tf ET', @CurrentFont['i'], @FontSizePt))
611
        end
612
    end
613

    
614
    def SetFontSize(size)
615
        # Set font size in points
616
        return if @FontSizePt==size
617
        @FontSizePt=size
618
        @FontSize=size/@k
619
        if @page>0
620
            out(sprintf('BT /F%d %.2f Tf ET',@CurrentFont['i'],@FontSizePt))
621
        end
622
    end
623

    
624
    def AddLink
625
        # Create a new internal link
626
        @links.push([0, 0])
627
        @links.size
628
    end
629

    
630
    def SetLink(link, y=0, page=-1)
631
        # Set destination of internal link
632
        y=@y if y==-1
633
        page=@page if page==-1
634
        @links[link]=[page, y]
635
    end
636

    
637
    def Link(x, y, w, h, link)
638
        # Put a link on the page
639
        @PageLinks[@page]=Array.new unless @PageLinks.has_key?(@Page)
640
        @PageLinks[@page].push([x*@k,@hPt-y*@k,w*@k,h*@k,link])
641
    end
642

    
643
    def Text(x, y, txt)
644
        # Output a string
645
        s=sprintf('BT %.2f %.2f Td (%s) Tj ET',x*@k,(@h-y)*@k, escape(txt));
646
        s=s+' '+dounderline(x,y,txt) if @underline and txt!=''
647
        s='q '+@TextColor+' '+s+' Q' if @ColorFlag
648
        out(s)
649
    end
650

    
651
    def AcceptPageBreak
652
        # Accept automatic page break or not
653
        @AutoPageBreak
654
    end
655

    
656
    def BreakThePage?(h)
657
      if (@y + h) > @PageBreakTrigger and !@InFooter and self.AcceptPageBreak
658
        true
659
      else
660
        false
661
      end
662
    end
663
    
664
    def Cell(w,h=0,txt='',border=0,ln=0,align='',fill=0,link='')
665
        # Output a cell
666
        if self.BreakThePage?(h)
667
            # Automatic page break
668
            x=@x
669
            ws=@ws
670
            if ws>0
671
                @ws=0
672
                out('0 Tw')
673
            end
674
            self.AddPage(@CurOrientation)
675
            @x=x
676
            if ws>0
677
                @ws=ws
678
                out(sprintf('%.3f Tw',ws*@k))
679
            end
680
        end
681
        w=@w-@rMargin-@x if w==0
682
        s=''
683
        if fill==1 or border==1
684
            if fill==1
685
                op=(border==1) ? 'B' : 'f'
686
            else
687
                op='S'
688
            end
689
            s=sprintf('%.2f %.2f %.2f %.2f re %s ',@x*@k,(@h-@y)*@k,w*@k,-h*@k,op)
690
        end
691
        if border.is_a? String
692
            x=@x
693
            y=@y
694
            unless border.index('L').nil?
695
                s=s+sprintf('%.2f %.2f m %.2f %.2f l S ',
696
                    x*@k,(@h-y)*@k,x*@k,(@h-(y+h))*@k)
697
            end
698
            unless border.index('T').nil?
699
                s=s+sprintf('%.2f %.2f m %.2f %.2f l S ',
700
                    x*@k,(@h-y)*@k,(x+w)*@k,(@h-y)*@k)
701
            end
702
            unless border.index('R').nil?
703
                s=s+sprintf('%.2f %.2f m %.2f %.2f l S ',
704
                    (x+w)*@k,(@h-y)*@k,(x+w)*@k,(@h-(y+h))*@k)
705
            end
706
            unless border.index('B').nil?
707
                s=s+sprintf('%.2f %.2f m %.2f %.2f l S ',
708
                    x*@k,(@h-(y+h))*@k,(x+w)*@k,(@h-(y+h))*@k)
709
            end
710
        end
711
        if txt!=''
712
            if align=='R'
713
                dx=w-@cMargin-self.GetStringWidth(txt)
714
            elsif align=='C'
715
                dx=(w-self.GetStringWidth(txt))/2
716
            else
717
                dx=@cMargin
718
            end
719
            if @ColorFlag
720
                s=s+'q '+@TextColor+' '
721
            end
722
            s=s+sprintf('BT %.2f %.2f Td (%s) Tj ET',
723
                (@x+dx)*@k,(@h-(@y+0.5*h+0.3*@FontSize))*@k,escape(txt))
724
            s=s+' '+dounderline(@x+dx,@y+0.5*h+0.3*@FontSize,txt) if @underline
725
            s=s+' Q' if @ColorFlag
726
            if link and link != ''
727
                Link(@x+dx,@y+0.5*h-0.5*@FontSize,GetStringWidth(txt),@FontSize,link)
728
            end
729
        end
730
        out(s) if s
731
        @lasth=h
732
        if ln>0
733
            # Go to next line
734
            @y=@y+h
735
            @x=@lMargin if ln==1
736
        else
737
            @x=@x+w
738
        end
739
    end
740

    
741
    def MultiCell(w,h,txt,border=0,align='J',fill=0)
742
        # Output text with automatic or explicit line breaks
743
        cw=@CurrentFont['cw']
744
        w=@w-@rMargin-@x if w==0
745
        wmax=(w-2*@cMargin)*1000/@FontSize
746
        s=txt.gsub("\r",'')
747
        nb=s.length
748
        nb=nb-1 if nb>0 and s[nb-1].chr=="\n"
749
        b=0
750
        if border!=0
751
            if border==1
752
                border='LTRB'
753
                b='LRT'
754
                b2='LR'
755
            else
756
                b2=''
757
                b2='L' unless border.index('L').nil?
758
                b2=b2+'R' unless border.index('R').nil?
759
                b=(not border.index('T').nil?) ? (b2+'T') : b2
760
            end
761
        end
762
        sep=-1
763
        to_index=0
764
        from_j=0
765
        l=0
766
        ns=0
767
        nl=1
768
        while to_index<nb
769
            # Get next character
770
            char=s[to_index]
771
            if char=="\n"[0]
772
                # Explicit line break
773
                if @ws>0
774
                    @ws=0
775
                    out('0 Tw')
776
                end
777
#Ed Moss               
778
                end_i = to_index == 0 ? 0 : to_index - 1
779
                # Changed from s[from_j..to_index] to fix bug reported by Hans Allis.
780
                self.Cell(w,h,s[from_j..end_i],b,2,align,fill) 
781
#                
782
                to_index=to_index+1
783
                sep=-1
784
                from_j=to_index
785
                l=0
786
                ns=0
787
                nl=nl+1
788
                b=b2 if border and nl==2
789
            else
790
                if char==' '[0]
791
                    sep=to_index
792
                    ls=l
793
                    ns=ns+1
794
                end
795
                l=l+GetCharWidth(cw, char)
796
                if l>wmax
797
                    # Automatic line break
798
                    if sep==-1
799
                        to_index=to_index+1 if to_index==from_j
800
                        if @ws>0
801
                            @ws=0
802
                            out('0 Tw')
803
                        end
804
#Ed Moss
805
                        self.Cell(w,h,s[from_j..to_index-1],b,2,align,fill)
806
#
807
                    else
808
                        if align=='J'
809
                            @ws=(ns>1) ? (wmax-ls)/1000.0*@FontSize/(ns-1) : 0
810
                            out(sprintf('%.3f Tw',@ws*@k))
811
                        end
812
                        self.Cell(w,h,s[from_j..sep],b,2,align,fill)
813
                        to_index=sep+1
814
                    end
815
                    sep=-1
816
                    from_j=to_index
817
                    l=0
818
                    ns=0
819
                    nl=nl+1
820
                    b=b2 if border and nl==2
821
                else
822
                    to_index=to_index+1
823
                end
824
            end
825
        end
826

    
827
        # Last chunk
828
        if @ws>0
829
            @ws=0
830
            out('0 Tw')
831
        end
832
        b=b+'B' if border!=0 and not border.index('B').nil?
833
        self.Cell(w,h,s[from_j..to_index],b,2,align,fill)
834
        @x=@lMargin
835
    end
836
    
837
    def Write(h,txt,link='')
838
        # Output text in flowing mode
839
        cw=@CurrentFont['cw']
840
        w=@w-@rMargin-@x
841
        wmax=(w-2*@cMargin)*1000/@FontSize
842
        s=txt.gsub("\r",'')
843
        nb=s.length
844
        sep=-1
845
        i=0
846
        j=0
847
        l=0
848
        nl=1
849
        while i<nb
850
            # Get next character
851
            c=s[i]
852
            if c=="\n"[0]
853
                # Explicit line break
854
                self.Cell(w,h,s[j,i-j],0,2,'',0,link)
855
                i=i+1
856
                sep=-1
857
                j=i
858
                l=0
859
                if nl==1
860
                    @x=@lMargin
861
                    w=@w-@rMargin-@x
862
                    wmax=(w-2*@cMargin)*1000/@FontSize
863
                end
864
                nl=nl+1
865
                next
866
            end
867
            if c==' '[0]
868
                sep=i
869
                ls=l
870
            end
871
            l=l+GetCharWidth(cw, c);
872
            if l>wmax
873
                # Automatic line break
874
                if sep==-1
875
                    if @x>@lMargin
876
                        # Move to next line
877
                        @x=@lMargin
878
                        @y=@y+h
879
                        w=@w-@rMargin-@x
880
                        wmax=(w-2*@cMargin)*1000/@FontSize
881
                        i=i+1
882
                        nl=nl+1
883
                        next
884
                    end
885
                    i=i+1 if i==j
886
                    self.Cell(w,h,s[j,i-j],0,2,'',0,link)
887
                else
888
                    self.Cell(w,h,s[j,sep-j],0,2,'',0,link)
889
                    i=sep+1
890
                end
891
                sep=-1
892
                j=i
893
                l=0
894
                if nl==1
895
                    @x=@lMargin
896
                    w=@w-@rMargin-@x
897
                    wmax=(w-2*@cMargin)*1000/@FontSize
898
                end
899
                nl=nl+1
900
            else
901
                i=i+1
902
            end
903
        end
904
        # Last chunk
905
        self.Cell(l/1000.0*@FontSize,h,s[j,i],0,0,'',0,link) if i!=j
906
    end
907
    
908
    def Image(file,x,y,w=0,h=0,type='',link='')
909
        # Put an image on the page
910
        unless @images.has_key?(file)
911
            # First use of image, get info
912
            if type==''
913
                pos=file.rindex('.')
914
                if pos.nil?
915
                    self.Error('Image file has no extension and no type was '+
916
                        'specified: '+file)
917
                end
918
                type=file[pos+1..-1]
919
            end
920
            type.downcase!
921
            if type=='jpg' or type=='jpeg'
922
                info=parsejpg(file)
923
            elsif type=='png'
924
                info=parsepng(file)
925
            else
926
                self.Error('Unsupported image file type: '+type)
927
            end
928
            info['i']=@images.length+1
929
            @images[file]=info
930
        else
931
            info=@images[file]
932
        end
933
#Ed Moss
934
        if(w==0 && h==0)
935
      		#Put image at 72 dpi
936
      		w=info['w']/@k;
937
      		h=info['h']/@k;
938
      	end
939
#
940
        # Automatic width or height calculation
941
        w=h*info['w']/info['h'] if w==0
942
        h=w*info['h']/info['w'] if h==0
943
        out(sprintf('q %.2f 0 0 %.2f %.2f %.2f cm /I%d Do Q',
944
            w*@k,h*@k,x*@k,(@h-(y+h))*@k,info['i']))
945
        Link(x,y,w,h,link) if link and link != ''
946
    end
947
    
948
    def Ln(h='')
949
        # Line feed; default value is last cell height
950
        @x=@lMargin
951
        if h.kind_of?(String)
952
            @y=@y+@lasth
953
        else
954
            @y=@y+h
955
        end
956
    end
957

    
958
    def GetX
959
        # Get x position
960
        @x
961
    end
962

    
963
    def SetX(x)
964
        # Set x position
965
        if x>=0
966
            @x=x
967
        else
968
            @x=@w+x
969
        end
970
    end
971

    
972
    def GetY
973
        # Get y position
974
        @y
975
    end
976

    
977
    def SetY(y)
978
        # Set y position and reset x
979
        @x=@lMargin
980
        if y>=0
981
            @y=y
982
        else
983
            @y=@h+y
984
        end
985
    end
986

    
987
    def SetXY(x,y)
988
        # Set x and y positions
989
        SetY(y)
990
        SetX(x)
991
    end
992
    
993
    def Output(file=nil)
994
        # Output PDF to file or return as a string
995
        
996
        # Finish document if necessary
997
        self.Close if(@state<3)
998
        
999
        if file.nil?
1000
            # Return as a string
1001
            return @buffer
1002
        else
1003
            # Save file locally
1004
            open(file,'wb') do |f|
1005
                f.write(@buffer)
1006
            end
1007
        end
1008
    end
1009

    
1010
    private
1011
  
1012
    def putpages
1013
        nb=@page
1014
        unless @AliasNbPages.nil? or @AliasNbPages==''
1015
            # Replace number of pages
1016
            1.upto(nb) do |n|
1017
                @pages[n].gsub!(@AliasNbPages,nb.to_s)
1018
            end
1019
        end
1020
        if @DefOrientation=='P'
1021
            wPt=@fwPt
1022
            hPt=@fhPt
1023
        else
1024
            wPt=@fhPt
1025
            hPt=@fwPt
1026
        end
1027
        filter=(@compress) ? '/Filter /FlateDecode ' : ''
1028
        1.upto(nb) do |n|
1029
            # Page
1030
            newobj
1031
            out('<</Type /Page')
1032
            out('/Parent 1 0 R')
1033
            unless @OrientationChanges[n].nil?
1034
                out(sprintf('/MediaBox [0 0 %.2f %.2f]',hPt,wPt))
1035
            end
1036
            out('/Resources 2 0 R')
1037
            if @PageLinks[n]
1038
                # Links
1039
                annots='/Annots ['
1040
                @PageLinks[n].each do |pl|
1041
                    rect=sprintf('%.2f %.2f %.2f %.2f',
1042
                        pl[0],pl[1],pl[0]+pl[2],pl[1]-pl[3])
1043
                    annots=annots+'<</Type /Annot /Subtype /Link /Rect ['+rect+
1044
                        '] /Border [0 0 0] '
1045
                    if pl[4].kind_of?(String)
1046
                        annots=annots+'/A <</S /URI /URI '+textstring(pl[4])+
1047
                            '>>>>'
1048
                    else
1049
                        l=@links[pl[4]]
1050
                        h=@OrientationChanges[l[0]].nil? ? hPt : wPt
1051
                        annots=annots+sprintf(
1052
                            '/Dest [%d 0 R /XYZ 0 %.2f null]>>',
1053
                            1+2*l[0],h-l[1]*@k)
1054
                    end
1055
                end
1056
                out(annots+']')
1057
            end
1058
            out('/Contents '+(@n+1).to_s+' 0 R>>')
1059
            out('endobj')
1060
            # Page content
1061
            p=(@compress) ? Zlib::Deflate.deflate(@pages[n]) : @pages[n]
1062
            newobj
1063
            out('<<'+filter+'/Length '+p.length.to_s+'>>')
1064
            putstream(p)
1065
            out('endobj')
1066
        end
1067
        # Pages root
1068
        @offsets[1]=@buffer.length
1069
        out('1 0 obj')
1070
        out('<</Type /Pages')
1071
        kids='/Kids ['
1072
        nb.times do |i|
1073
            kids=kids+(3+2*i).to_s+' 0 R '
1074
        end
1075
        out(kids+']')
1076
        out('/Count '+nb.to_s)
1077
        out(sprintf('/MediaBox [0 0 %.2f %.2f]',wPt,hPt))
1078
        out('>>')
1079
        out('endobj')
1080
    end
1081
    
1082
    def putfonts
1083
        nf=@n
1084
        @diffs.each do |diff|
1085
            # Encodings
1086
            newobj
1087
            out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences '+
1088
                '['+diff+']>>')
1089
            out('endobj')
1090
        end
1091

    
1092
        @FontFiles.each do |file, info|
1093
            # Font file embedding
1094
            newobj
1095
            @FontFiles[file]['n'] = @n
1096

    
1097
            if self.class.const_defined? 'FPDF_FONTPATH' then
1098
                if FPDF_FONTPATH[-1,1] == '/' then
1099
                    file = FPDF_FONTPATH + file
1100
                else
1101
                    file = FPDF_FONTPATH + '/' + file
1102
                end
1103
            end
1104

    
1105
            size = File.size(file)
1106
            unless File.exists?(file)
1107
                Error('Font file not found')
1108
            end
1109

    
1110
            out('<</Length ' + size.to_s)
1111

    
1112
            if file[-2, 2] == '.z' then
1113
                out('/Filter /FlateDecode')
1114
            end
1115
            out('/Length1 ' + info['length1'])
1116
            out('/Length2 ' + info['length2'] + ' /Length3 0') if info['length2']
1117
            out('>>')
1118
            open(file, 'rb') do |f|
1119
                putstream(f.read())
1120
            end
1121
            out('endobj')
1122
        end
1123

    
1124
        file = 0
1125
        @fonts.each do |k, font|
1126
            # Font objects
1127
            @fonts[k]['n']=@n+1
1128
            type=font['type']
1129
            name=font['name']
1130
            if type=='core'
1131
                # Standard font
1132
                newobj
1133
                out('<</Type /Font')
1134
                out('/BaseFont /'+name)
1135
                out('/Subtype /Type1')
1136
                if name!='Symbol' and name!='ZapfDingbats'
1137
                    out('/Encoding /WinAnsiEncoding')
1138
                end
1139
                out('>>')
1140
                out('endobj')
1141
            elsif type=='Type1' or type=='TrueType'
1142
                # Additional Type1 or TrueType font
1143
                newobj
1144
                out('<</Type /Font')
1145
                out('/BaseFont /'+name)
1146
                out('/Subtype /'+type)
1147
                out('/FirstChar 32 /LastChar 255')
1148
                out('/Widths '+(@n+1).to_s+' 0 R')
1149
                out('/FontDescriptor '+(@n+2).to_s+' 0 R')
1150
                if font['enc'] and font['enc'] != ''
1151
                    unless font['diff'].nil?
1152
                        out('/Encoding '+(nf+font['diff']).to_s+' 0 R')
1153
                    else
1154
                        out('/Encoding /WinAnsiEncoding')
1155
                    end
1156
                end
1157
                out('>>')
1158
                out('endobj')
1159
                # Widths
1160
                newobj
1161
                cw=font['cw']
1162
                s='['
1163
                32.upto(255) do |i|
1164
                    s << GetCharWidth(cw, i).to_s + ' '
1165
                end
1166
                out(s+']')
1167
                out('endobj')
1168
                # Descriptor
1169
                newobj
1170
                s='<</Type /FontDescriptor /FontName /'+name
1171
                font['desc'].each do |k, v|
1172
                    s << ' /'+k+' '+v
1173
                end
1174
                file=font['file']
1175
                if file
1176
                    s << ' /FontFile'+(type=='Type1' ? '' : '2')+' '+
1177
                        @FontFiles[file]['n'].to_s+' 0 R'
1178
                end
1179
                out(s+'>>')
1180
                out('endobj')
1181
            else
1182
                # Allow for additional types
1183
                mtd='put'+type.downcase
1184
                unless self.respond_to?(mtd)
1185
                    self.Error('Unsupported font type: '+type)
1186
                end
1187
                self.send(mtd, font)
1188
            end
1189
        end
1190
    end
1191
    
1192
    def putimages
1193
        filter=(@compress) ? '/Filter /FlateDecode ' : ''
1194
        @images.each do |file, info|
1195
            newobj
1196
            @images[file]['n']=@n
1197
            out('<</Type /XObject')
1198
            out('/Subtype /Image')
1199
            out('/Width '+info['w'].to_s)
1200
            out('/Height '+info['h'].to_s)
1201
            if info['cs']=='Indexed'
1202
                out("/ColorSpace [/Indexed /DeviceRGB #{info['pal'].length/3-1} #{(@n+1)} 0 R]")
1203
            else
1204
                out('/ColorSpace /'+info['cs'])
1205
                if info['cs']=='DeviceCMYK'
1206
                    out('/Decode [1 0 1 0 1 0 1 0]')
1207
                end
1208
            end
1209
            out('/BitsPerComponent '+info['bpc'].to_s)
1210
            out('/Filter /'+info['f']) if info['f']
1211
            unless info['parms'].nil?
1212
                out(info['parms'])
1213
            end
1214
            if info['trns'] and info['trns'].kind_of?(Array)
1215
                trns=''
1216
                info['trns'].length.times do |i|
1217
                    trns=trns+info['trns'][i].to_s+' '+info['trns'][i].to_s+' '
1218
                end
1219
                out('/Mask ['+trns+']')
1220
            end
1221
            out('/Length '+info['data'].length.to_s+'>>')
1222
            putstream(info['data'])
1223
            @images[file]['data']=nil
1224
            out('endobj')
1225
            # Palette
1226
            if info['cs']=='Indexed'
1227
                newobj
1228
                pal=(@compress) ? Zlib::Deflate.deflate(info['pal']) : info['pal']
1229
                out('<<'+filter+'/Length '+pal.length.to_s+'>>')
1230
                putstream(pal)
1231
                out('endobj')
1232
            end
1233
        end
1234
    end
1235

    
1236
    def putxobjectdict
1237
        @images.each_value do |image|
1238
            out('/I'+image['i'].to_s+' '+image['n'].to_s+' 0 R')
1239
        end
1240
    end
1241

    
1242
    def putresourcedict
1243
        out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]')
1244
        out('/Font <<')
1245
        @fonts.each_value do |font|
1246
            out('/F'+font['i'].to_s+' '+font['n'].to_s+' 0 R')
1247
        end
1248
        out('>>')
1249
        out('/XObject <<')
1250
        putxobjectdict
1251
        out('>>')
1252
    end
1253

    
1254
    def putresources
1255
        putfonts
1256
        putimages
1257
        # Resource dictionary
1258
        @offsets[2]=@buffer.length
1259
        out('2 0 obj')
1260
        out('<<')
1261
        putresourcedict
1262
        out('>>')
1263
        out('endobj')
1264
    end
1265
    
1266
    def putinfo
1267
        out('/Producer '+textstring('Ruby FPDF '+FPDF_VERSION));
1268
        unless @title.nil?
1269
            out('/Title '+textstring(@title))
1270
        end
1271
        unless @subject.nil?
1272
            out('/Subject '+textstring(@subject))
1273
        end
1274
        unless @author.nil?
1275
            out('/Author '+textstring(@author))
1276
        end
1277
        unless @keywords.nil?
1278
            out('/Keywords '+textstring(@keywords))
1279
        end
1280
        unless @creator.nil?
1281
            out('/Creator '+textstring(@creator))
1282
        end
1283
        out('/CreationDate '+textstring('D: '+DateTime.now.to_s))
1284
    end
1285
    
1286
    def putcatalog
1287
        out('/Type /Catalog')
1288
        out('/Pages 1 0 R')
1289
        if @ZoomMode=='fullpage'
1290
            out('/OpenAction [3 0 R /Fit]')
1291
        elsif @ZoomMode=='fullwidth'
1292
            out('/OpenAction [3 0 R /FitH null]')
1293
        elsif @ZoomMode=='real'
1294
            out('/OpenAction [3 0 R /XYZ null null 1]')
1295
        elsif not @ZoomMode.kind_of?(String)
1296
            out('/OpenAction [3 0 R /XYZ null null '+(@ZoomMode/100)+']')
1297
        end
1298
        
1299
        if @LayoutMode=='single'
1300
            out('/PageLayout /SinglePage')
1301
        elsif @LayoutMode=='continuous'
1302
            out('/PageLayout /OneColumn')
1303
        elsif @LayoutMode=='two'
1304
            out('/PageLayout /TwoColumnLeft')
1305
        end
1306
    end
1307

    
1308
    def putheader
1309
        out('%PDF-'+@PDFVersion)
1310
    end
1311

    
1312
    def puttrailer
1313
        out('/Size '+(@n+1).to_s)
1314
        out('/Root '+@n.to_s+' 0 R')
1315
        out('/Info '+(@n-1).to_s+' 0 R')
1316
    end
1317

    
1318
    def enddoc
1319
        putheader
1320
        putpages
1321
        putresources
1322
        # Info
1323
        newobj
1324
        out('<<')
1325
        putinfo
1326
        out('>>')
1327
        out('endobj')
1328
        # Catalog
1329
        newobj
1330
        out('<<')
1331
        putcatalog
1332
        out('>>')
1333
        out('endobj')
1334
        # Cross-ref
1335
        o=@buffer.length
1336
        out('xref')
1337
        out('0 '+(@n+1).to_s)
1338
        out('0000000000 65535 f ')
1339
        1.upto(@n) do |i|
1340
            out(sprintf('%010d 00000 n ',@offsets[i]))
1341
        end
1342
        # Trailer
1343
        out('trailer')
1344
        out('<<')
1345
        puttrailer
1346
        out('>>')
1347
        out('startxref')
1348
        out(o)
1349
        out('%%EOF')
1350
        @state=3
1351
    end
1352

    
1353
    def beginpage(orientation)
1354
        @page=@page+1
1355
        @pages[@page]=''
1356
        @state=2
1357
        @x=@lMargin
1358
        @y=@tMargin
1359
        @lasth=0
1360
        @FontFamily=''
1361
        # Page orientation
1362
        if orientation==''
1363
            orientation=@DefOrientation
1364
        else
1365
            orientation=orientation[0].chr.upcase
1366
            if orientation!=@DefOrientation
1367
                @OrientationChanges[@page]=true
1368
            end
1369
        end
1370
        if orientation!=@CurOrientation
1371
            # Change orientation
1372
            if orientation=='P'
1373
                @wPt=@fwPt
1374
                @hPt=@fhPt
1375
                @w=@fw
1376
                @h=@fh
1377
            else
1378
                @wPt=@fhPt
1379
                @hPt=@fwPt
1380
                @w=@fh
1381
                @h=@fw
1382
            end
1383
            @PageBreakTrigger=@h-@bMargin
1384
            @CurOrientation=orientation
1385
        end
1386
    end
1387
    
1388
    def endpage
1389
        # End of page contents
1390
        @state=1
1391
    end
1392
    
1393
    def newobj
1394
        # Begin a new object
1395
        @n=@n+1
1396
        @offsets[@n]=@buffer.length
1397
        out(@n.to_s+' 0 obj')
1398
    end
1399

    
1400
    def dounderline(x,y,txt)
1401
        # Underline text
1402
        up=@CurrentFont['up']
1403
        ut=@CurrentFont['ut']
1404
        w=GetStringWidth(txt)+@ws*txt.count(' ')
1405
        sprintf('%.2f %.2f %.2f %.2f re f',
1406
            x*@k,(@h-(y-up/1000.0*@FontSize))*@k,w*@k,-ut/1000.0*@FontSizePt)
1407
    end
1408
    
1409
    def parsejpg(file)
1410
        # Extract info from a JPEG file
1411
        a=extractjpginfo(file)
1412
        raise "Missing or incorrect JPEG file: #{file}" if a.nil?
1413

    
1414
        if a['channels'].nil? || a['channels']==3 then
1415
            colspace='DeviceRGB'
1416
        elsif a['channels']==4 then
1417
            colspace='DeviceCMYK'
1418
        else
1419
            colspace='DeviceGray'
1420
        end
1421
        bpc= a['bits'] ? a['bits'].to_i : 8
1422

    
1423
        # Read whole file
1424
        data = nil
1425
        open(file, 'rb') do |f|
1426
            data = f.read
1427
        end
1428
        return {'w'=>a['width'],'h'=>a['height'],'cs'=>colspace,'bpc'=>bpc,'f'=>'DCTDecode','data'=>data}
1429
    end
1430

    
1431
    def parsepng(file)
1432
        # Extract info from a PNG file
1433
        f=open(file,'rb')
1434
        # Check signature
1435
        unless f.read(8)==137.chr+'PNG'+13.chr+10.chr+26.chr+10.chr
1436
            self.Error('Not a PNG file: '+file)
1437
        end
1438
        # Read header chunk
1439
        f.read(4)
1440
        if f.read(4)!='IHDR'
1441
            self.Error('Incorrect PNG file: '+file)
1442
        end
1443
        w=freadint(f)
1444
        h=freadint(f)
1445
        bpc=f.read(1)[0]
1446
        if bpc>8
1447
            self.Error('16-bit depth not supported: '+file)
1448
        end
1449
        ct=f.read(1)[0]
1450
        if ct==0
1451
            colspace='DeviceGray'
1452
        elsif ct==2
1453
            colspace='DeviceRGB'
1454
        elsif ct==3
1455
            colspace='Indexed'
1456
        else
1457
            self.Error('Alpha channel not supported: '+file)
1458
        end
1459
        if f.read(1)[0]!=0
1460
            self.Error('Unknown compression method: '+file)
1461
        end
1462
        if f.read(1)[0]!=0
1463
            self.Error('Unknown filter method: '+file)
1464
        end
1465
        if f.read(1)[0]!=0
1466
            self.Error('Interlacing not supported: '+file)
1467
        end
1468
        f.read(4)
1469
        parms='/DecodeParms <</Predictor 15 /Colors '+(ct==2 ? '3' : '1')+
1470
            ' /BitsPerComponent '+bpc.to_s+' /Columns '+w.to_s+'>>'
1471
        # Scan chunks looking for palette, transparency and image data
1472
        pal=''
1473
        trns=''
1474
        data=''
1475
        begin
1476
            n=freadint(f)
1477
            type=f.read(4)
1478
            if type=='PLTE'
1479
                # Read palette
1480
                pal=f.read(n)
1481
                f.read(4)
1482
            elsif type=='tRNS'
1483
                # Read transparency info
1484
                t=f.read(n)
1485
                if ct==0
1486
                    trns=[t[1]]
1487
                elsif ct==2
1488
                    trns=[t[1],t[3],t[5]]
1489
                else
1490
                    pos=t.index(0)
1491
                    trns=[pos] unless pos.nil?
1492
                end
1493
                f.read(4)
1494
            elsif type=='IDAT'
1495
                # Read image data block
1496
                data << f.read(n)
1497
                f.read(4)
1498
            elsif type=='IEND'
1499
                break
1500
            else
1501
                f.read(n+4)
1502
            end
1503
        end while n
1504
        if colspace=='Indexed' and pal==''
1505
            self.Error('Missing palette in '+file)
1506
        end
1507
        f.close
1508
        {'w'=>w,'h'=>h,'cs'=>colspace,'bpc'=>bpc,'f'=>'FlateDecode',
1509
            'parms'=>parms,'pal'=>pal,'trns'=>trns,'data'=>data}
1510
    end
1511

    
1512
    def freadint(f)
1513
        # Read a 4-byte integer from file
1514
        a = f.read(4).unpack('N')
1515
        return a[0]
1516
    end
1517

    
1518
    def freadshort(f)
1519
        a = f.read(2).unpack('n')
1520
        return a[0]
1521
    end
1522

    
1523
    def freadbyte(f)
1524
        a = f.read(1).unpack('C')
1525
        return a[0]
1526
    end
1527

    
1528
    def textstring(s)
1529
        # Format a text string
1530
        '('+escape(s)+')'
1531
    end
1532

    
1533
    def escape(s)
1534
        # Add \ before \, ( and )
1535
        s.gsub('\\','\\\\\\').gsub('(','\\(').gsub(')','\\)')
1536
    end
1537

    
1538
    def putstream(s)
1539
        out('stream')
1540
        out(s)
1541
        out('endstream')
1542
    end
1543

    
1544
    def out(s)
1545
        # Add a line to the document
1546
        if @state==2
1547
            @pages[@page]=@pages[@page]+s+"\n"
1548
        else
1549
            @buffer=@buffer+s.to_s+"\n"
1550
        end
1551
    end
1552

    
1553
    # jpeg marker codes
1554

    
1555
    M_SOF0  = 0xc0
1556
    M_SOF1  = 0xc1
1557
    M_SOF2  = 0xc2
1558
    M_SOF3  = 0xc3
1559

    
1560
    M_SOF5  = 0xc5
1561
    M_SOF6  = 0xc6
1562
    M_SOF7  = 0xc7
1563

    
1564
    M_SOF9  = 0xc9
1565
    M_SOF10 = 0xca
1566
    M_SOF11 = 0xcb
1567

    
1568
    M_SOF13 = 0xcd
1569
    M_SOF14 = 0xce
1570
    M_SOF15 = 0xcf
1571

    
1572
    M_SOI   = 0xd8
1573
    M_EOI   = 0xd9
1574
    M_SOS   = 0xda
1575

    
1576
    def extractjpginfo(file)
1577
        result = nil
1578

    
1579
        open(file, "rb") do |f|
1580
            marker = jpegnextmarker(f)
1581

    
1582
            if marker != M_SOI
1583
                return nil
1584
            end
1585

    
1586
            while true
1587
                marker = jpegnextmarker(f)
1588

    
1589
                case marker
1590
                  when M_SOF0,  M_SOF1,  M_SOF2,  M_SOF3,
1591
                       M_SOF5,  M_SOF6,  M_SOF7,  M_SOF9,
1592
                     M_SOF10, M_SOF11, M_SOF13, M_SOF14,
1593
                     M_SOF15 then
1594

    
1595
                    length = freadshort(f)
1596

    
1597
                    if result.nil?
1598
                        result = {}
1599

    
1600
                        result['bits']     = freadbyte(f)
1601
                        result['height']   = freadshort(f)
1602
                        result['width']    = freadshort(f)
1603
                        result['channels'] = freadbyte(f)
1604

    
1605
                        f.seek(length - 8, IO::SEEK_CUR)
1606
                    else
1607
                        f.seek(length - 2, IO::SEEK_CUR)
1608
                    end
1609
                when M_SOS, M_EOI then
1610
                    return result
1611
                else
1612
                    length = freadshort(f)
1613
                    f.seek(length - 2, IO::SEEK_CUR)
1614
                end
1615
            end
1616
        end
1617
    end
1618

    
1619
    def jpegnextmarker(f)
1620
        while true
1621
            # look for 0xff
1622
            while (c = freadbyte(f)) != 0xff
1623
            end
1624

    
1625
            c = freadbyte(f)
1626

    
1627
            if c != 0
1628
                return c
1629
            end
1630
        end
1631
    end
1632
end