To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.
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 |