eideticpdf 0.9.9
Sign up to get free protection for your applications and to get access to all the features.
- data/fonts/AntiqueOlive-Bold.afm +406 -0
- data/fonts/AntiqueOlive-Bold.inf +27 -0
- data/fonts/AntiqueOlive-Compact.afm +401 -0
- data/fonts/AntiqueOlive-Compact.inf +27 -0
- data/fonts/AntiqueOlive-Italic.afm +401 -0
- data/fonts/AntiqueOlive-Italic.inf +27 -0
- data/fonts/AntiqueOlive-Roman.afm +409 -0
- data/fonts/AntiqueOlive-Roman.inf +27 -0
- data/fonts/AvantGarde-Book.afm +667 -0
- data/fonts/AvantGarde-Book.inf +26 -0
- data/fonts/AvantGarde-BookOblique.afm +667 -0
- data/fonts/AvantGarde-BookOblique.inf +26 -0
- data/fonts/AvantGarde-Demi.afm +673 -0
- data/fonts/AvantGarde-Demi.inf +26 -0
- data/fonts/AvantGarde-DemiOblique.afm +673 -0
- data/fonts/AvantGarde-DemiOblique.inf +26 -0
- data/fonts/Bookman-Demi.afm +669 -0
- data/fonts/Bookman-Demi.inf +25 -0
- data/fonts/Bookman-DemiItalic.afm +669 -0
- data/fonts/Bookman-DemiItalic.inf +25 -0
- data/fonts/Bookman-Light.afm +643 -0
- data/fonts/Bookman-Light.inf +25 -0
- data/fonts/Bookman-LightItalic.afm +620 -0
- data/fonts/Bookman-LightItalic.inf +25 -0
- data/fonts/Clarendon-Bold.afm +412 -0
- data/fonts/Clarendon-Bold.inf +27 -0
- data/fonts/Clarendon-Light.afm +410 -0
- data/fonts/Clarendon-Light.inf +27 -0
- data/fonts/Clarendon.afm +410 -0
- data/fonts/Clarendon.inf +27 -0
- data/fonts/CooperBlack-Italic.afm +421 -0
- data/fonts/CooperBlack-Italic.inf +26 -0
- data/fonts/CooperBlack.afm +427 -0
- data/fonts/CooperBlack.inf +26 -0
- data/fonts/Coronet-Regular.afm +327 -0
- data/fonts/Coronet-Regular.inf +25 -0
- data/fonts/Courier-Bold.afm +342 -0
- data/fonts/Courier-Bold.inf +26 -0
- data/fonts/Courier-BoldOblique.afm +342 -0
- data/fonts/Courier-BoldOblique.inf +26 -0
- data/fonts/Courier-Oblique.afm +342 -0
- data/fonts/Courier-Oblique.inf +26 -0
- data/fonts/Courier.afm +342 -0
- data/fonts/Courier.inf +26 -0
- data/fonts/Eurostile.afm +419 -0
- data/fonts/Eurostile.inf +27 -0
- data/fonts/Goudy-ExtraBold.afm +412 -0
- data/fonts/Goudy-ExtraBold.inf +27 -0
- data/fonts/Helvetica-Bold.afm +2827 -0
- data/fonts/Helvetica-Bold.inf +26 -0
- data/fonts/Helvetica-BoldOblique.afm +2827 -0
- data/fonts/Helvetica-BoldOblique.inf +26 -0
- data/fonts/Helvetica-Condensed-Bold.afm +623 -0
- data/fonts/Helvetica-Condensed-Bold.inf +25 -0
- data/fonts/Helvetica-Condensed-BoldObl.afm +623 -0
- data/fonts/Helvetica-Condensed-BoldObl.inf +25 -0
- data/fonts/Helvetica-Condensed-Oblique.afm +528 -0
- data/fonts/Helvetica-Condensed-Oblique.inf +25 -0
- data/fonts/Helvetica-Condensed.afm +528 -0
- data/fonts/Helvetica-Condensed.inf +25 -0
- data/fonts/Helvetica-Narrow-Bold.afm +1439 -0
- data/fonts/Helvetica-Narrow-Bold.inf +26 -0
- data/fonts/Helvetica-Narrow-BoldOblique.afm +1439 -0
- data/fonts/Helvetica-Narrow-BoldOblique.inf +26 -0
- data/fonts/Helvetica-Narrow-Oblique.afm +1556 -0
- data/fonts/Helvetica-Narrow-Oblique.inf +26 -0
- data/fonts/Helvetica-Narrow.afm +1556 -0
- data/fonts/Helvetica-Narrow.inf +26 -0
- data/fonts/Helvetica-Oblique.afm +3051 -0
- data/fonts/Helvetica-Oblique.inf +26 -0
- data/fonts/Helvetica.afm +3051 -0
- data/fonts/Helvetica.inf +26 -0
- data/fonts/LetterGothic-Bold.afm +443 -0
- data/fonts/LetterGothic-Bold.inf +26 -0
- data/fonts/LetterGothic-BoldSlanted.afm +443 -0
- data/fonts/LetterGothic-BoldSlanted.inf +26 -0
- data/fonts/LetterGothic-Slanted.afm +443 -0
- data/fonts/LetterGothic-Slanted.inf +26 -0
- data/fonts/LetterGothic.afm +443 -0
- data/fonts/LetterGothic.inf +26 -0
- data/fonts/LubalinGraph-Book.afm +351 -0
- data/fonts/LubalinGraph-Book.inf +25 -0
- data/fonts/LubalinGraph-BookOblique.afm +351 -0
- data/fonts/LubalinGraph-BookOblique.inf +25 -0
- data/fonts/LubalinGraph-Demi.afm +351 -0
- data/fonts/LubalinGraph-Demi.inf +25 -0
- data/fonts/LubalinGraph-DemiOblique.afm +351 -0
- data/fonts/LubalinGraph-DemiOblique.inf +25 -0
- data/fonts/Marigold.afm +491 -0
- data/fonts/Marigold.inf +27 -0
- data/fonts/MonaLisa-Recut.afm +663 -0
- data/fonts/MonaLisa-Recut.inf +27 -0
- data/fonts/NewCenturySchlbk-Bold.afm +886 -0
- data/fonts/NewCenturySchlbk-Bold.inf +25 -0
- data/fonts/NewCenturySchlbk-BoldItalic.afm +1521 -0
- data/fonts/NewCenturySchlbk-BoldItalic.inf +26 -0
- data/fonts/NewCenturySchlbk-Italic.afm +1113 -0
- data/fonts/NewCenturySchlbk-Italic.inf +26 -0
- data/fonts/NewCenturySchlbk-Roman.afm +1067 -0
- data/fonts/NewCenturySchlbk-Roman.inf +26 -0
- data/fonts/Optima-Bold.afm +515 -0
- data/fonts/Optima-Bold.inf +27 -0
- data/fonts/Optima-BoldItalic.afm +712 -0
- data/fonts/Optima-BoldItalic.inf +26 -0
- data/fonts/Optima-Italic.afm +737 -0
- data/fonts/Optima-Italic.inf +26 -0
- data/fonts/Optima.afm +501 -0
- data/fonts/Optima.inf +27 -0
- data/fonts/Palatino-Bold.afm +675 -0
- data/fonts/Palatino-Bold.inf +26 -0
- data/fonts/Palatino-BoldItalic.afm +702 -0
- data/fonts/Palatino-BoldItalic.inf +26 -0
- data/fonts/Palatino-Italic.afm +700 -0
- data/fonts/Palatino-Italic.inf +26 -0
- data/fonts/Palatino-Roman.afm +718 -0
- data/fonts/Palatino-Roman.inf +26 -0
- data/fonts/StempelGaramond-Bold.afm +412 -0
- data/fonts/StempelGaramond-Bold.inf +27 -0
- data/fonts/StempelGaramond-BoldItalic.afm +413 -0
- data/fonts/StempelGaramond-BoldItalic.inf +27 -0
- data/fonts/StempelGaramond-Italic.afm +413 -0
- data/fonts/StempelGaramond-Italic.inf +27 -0
- data/fonts/StempelGaramond-Roman.afm +412 -0
- data/fonts/StempelGaramond-Roman.inf +27 -0
- data/fonts/Symbol.afm +213 -0
- data/fonts/Symbol.inf +27 -0
- data/fonts/Times-Bold.afm +2588 -0
- data/fonts/Times-Bold.inf +26 -0
- data/fonts/Times-BoldItalic.afm +2384 -0
- data/fonts/Times-BoldItalic.inf +26 -0
- data/fonts/Times-Italic.afm +2667 -0
- data/fonts/Times-Italic.inf +26 -0
- data/fonts/Times-Roman.afm +2419 -0
- data/fonts/Times-Roman.inf +26 -0
- data/fonts/Univers-Bold.afm +712 -0
- data/fonts/Univers-Bold.inf +27 -0
- data/fonts/Univers-BoldOblique.afm +712 -0
- data/fonts/Univers-BoldOblique.inf +27 -0
- data/fonts/Univers-Condensed.afm +403 -0
- data/fonts/Univers-Condensed.inf +27 -0
- data/fonts/Univers-CondensedOblique.afm +403 -0
- data/fonts/Univers-CondensedOblique.inf +27 -0
- data/fonts/Univers-Light.afm +737 -0
- data/fonts/Univers-Light.inf +27 -0
- data/fonts/Univers-LightOblique.afm +737 -0
- data/fonts/Univers-LightOblique.inf +27 -0
- data/fonts/Univers-Oblique.afm +656 -0
- data/fonts/Univers-Oblique.inf +27 -0
- data/fonts/Univers.afm +656 -0
- data/fonts/Univers.inf +27 -0
- data/fonts/ZapfChancery-MediumItalic.afm +842 -0
- data/fonts/ZapfChancery-MediumItalic.inf +26 -0
- data/fonts/ZapfDingbats.afm +225 -0
- data/fonts/ZapfDingbats.inf +26 -0
- data/lib/epdfafm.rb +334 -0
- data/lib/epdfdw.rb +710 -0
- data/lib/epdfk.rb +3142 -0
- data/lib/epdfo.rb +882 -0
- data/lib/epdfpw.rb +1749 -0
- data/lib/epdfs.rb +101 -0
- data/lib/epdfsw.rb +267 -0
- data/lib/epdft.rb +145 -0
- data/lib/epdftt.rb +458 -0
- data/test/pdf_tests.rb +16 -0
- data/test/test.rb +816 -0
- data/test/test_epdfafm.rb +202 -0
- data/test/test_epdfdw.rb +369 -0
- data/test/test_epdfk.rb +47 -0
- data/test/test_epdfo.rb +762 -0
- data/test/test_epdfpw.rb +129 -0
- data/test/test_epdfs.rb +54 -0
- data/test/test_epdfsw.rb +314 -0
- data/test/test_epdft.rb +198 -0
- data/test/test_epdftt.rb +34 -0
- data/test/test_helpers.rb +18 -0
- data/test/testimg.jpg +0 -0
- metadata +247 -0
data/lib/epdfs.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: ASCII-8BIT
|
3
|
+
#
|
4
|
+
# Created by Brent Rowland on 2008-01-12.
|
5
|
+
# Copyright (c) 2008, Eidetic Software. All rights reserved.
|
6
|
+
|
7
|
+
# Eidetic PDF Support
|
8
|
+
|
9
|
+
class Numeric # :nodoc:
|
10
|
+
unless 1.respond_to?(:degrees)
|
11
|
+
def degrees
|
12
|
+
self * Math::PI / 180
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
unless 2.respond_to?(:even?)
|
17
|
+
def even?
|
18
|
+
self % 2 == 0
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
unless 1.respond_to?(:odd?)
|
23
|
+
def odd?
|
24
|
+
self % 2 != 0
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
module EideticPDF
|
30
|
+
ImageReadMode = "".respond_to?(:encoding) ? "rb:binary" : "rb"
|
31
|
+
|
32
|
+
module Statistics # :nodoc:
|
33
|
+
def sum
|
34
|
+
self.inject(0) { |total, obj| total + obj }
|
35
|
+
end
|
36
|
+
|
37
|
+
def mean
|
38
|
+
self.sum.quo(self.size)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class PropertyStack # :nodoc:
|
43
|
+
def initialize(obj, prop, &block)
|
44
|
+
@obj, @prop, @condition = obj, prop, block
|
45
|
+
@stack = []
|
46
|
+
end
|
47
|
+
|
48
|
+
def push(value)
|
49
|
+
@stack.push @obj.send(@prop)
|
50
|
+
@obj.send(@prop, value) if @condition.call(value)
|
51
|
+
end
|
52
|
+
|
53
|
+
def pop
|
54
|
+
value = @stack.pop
|
55
|
+
@obj.send(@prop, value) if @condition.call(value)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class ColorStack # :nodoc:
|
60
|
+
def initialize(obj, prop)
|
61
|
+
@obj, @prop = obj, prop
|
62
|
+
@stack = []
|
63
|
+
end
|
64
|
+
|
65
|
+
def push(color)
|
66
|
+
@stack.push @obj.send(@prop)
|
67
|
+
@obj.send(@prop, color) if color.respond_to?(:to_int) or color.respond_to?(:to_str)
|
68
|
+
end
|
69
|
+
|
70
|
+
def pop
|
71
|
+
color = @stack.pop
|
72
|
+
@obj.send(@prop, color) if color.respond_to?(:to_int) or color.respond_to?(:to_str)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
module JpegInfo # :nodoc:
|
77
|
+
def jpeg?(image)
|
78
|
+
# image[0, 2] == "\xFF\xD8"
|
79
|
+
image[0, 2].hash == "\xFF\xD8".hash
|
80
|
+
end
|
81
|
+
|
82
|
+
def jpeg_dimensions(image)
|
83
|
+
raise ArgumentError, "Not a JPEG" unless jpeg?(image)
|
84
|
+
image = image.dup
|
85
|
+
image.slice!(0, 2) # delete jpeg marker
|
86
|
+
while marker = image.slice!(0, 4)
|
87
|
+
m, c, l = marker.unpack('aan')
|
88
|
+
raise "Bad JPEG" unless m == "\xFF"
|
89
|
+
if ["\xC0", "\xC1", "\xC2", "\xC3", "\xC5", "\xC6", "\xC7", "\xC9", "\xCA", "\xCB", "\xCD", "\xCE", "\xCF"].include?(c)
|
90
|
+
dims = image.slice(0, 6)
|
91
|
+
bits_per_component, height, width, components = dims.unpack('CnnC')
|
92
|
+
break
|
93
|
+
end
|
94
|
+
image.slice!(0, l - 2)
|
95
|
+
end
|
96
|
+
[width, height, components, bits_per_component]
|
97
|
+
end
|
98
|
+
|
99
|
+
module_function :jpeg?, :jpeg_dimensions
|
100
|
+
end
|
101
|
+
end
|
data/lib/epdfsw.rb
ADDED
@@ -0,0 +1,267 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: ASCII-8BIT
|
3
|
+
#
|
4
|
+
# Created by Brent Rowland on 2007-07-13.
|
5
|
+
# Copyright (c) 2007, 2008 Eidetic Software. All rights reserved.
|
6
|
+
#
|
7
|
+
# Eidetic PDF Stream Writers
|
8
|
+
|
9
|
+
require 'epdfo'
|
10
|
+
|
11
|
+
module EideticPDF
|
12
|
+
class BaseWriter # :nodoc:
|
13
|
+
def f(value, prec=2)
|
14
|
+
sprintf("%.*f", prec, value).sub(',','.')
|
15
|
+
end
|
16
|
+
|
17
|
+
def g(value, prec=4)
|
18
|
+
# prec must be >= 1 or gsub will strip significant trailing 0's.
|
19
|
+
sprintf("%.*f", prec, value).sub(',','.').gsub(/\.?0*$/,'')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class MiscWriter < BaseWriter # :nodoc:
|
24
|
+
def initialize(stream)
|
25
|
+
@stream = stream
|
26
|
+
end
|
27
|
+
|
28
|
+
def set_gray_fill(gray)
|
29
|
+
@stream << "%s g\n" % g(gray)
|
30
|
+
end
|
31
|
+
|
32
|
+
def set_gray_stroke(gray)
|
33
|
+
@stream << "%s G\n" % g(gray)
|
34
|
+
end
|
35
|
+
|
36
|
+
def set_cmyk_color_fill(c, m, y, k)
|
37
|
+
@stream << "%s %s %s %s k\n" % [g(c), g(m), g(y), g(k)]
|
38
|
+
end
|
39
|
+
|
40
|
+
def set_cmyk_color_stroke(c, m, y, k)
|
41
|
+
@stream << "%s %s %s %s K\n" % [g(c), g(m), g(y), g(k)]
|
42
|
+
end
|
43
|
+
|
44
|
+
def set_rgb_color_fill(red, green, blue)
|
45
|
+
@stream << "%s %s %s rg\n" % [g(red), g(green), g(blue)]
|
46
|
+
end
|
47
|
+
|
48
|
+
def set_rgb_color_stroke(red, green, blue)
|
49
|
+
@stream << "%s %s %s RG\n" % [g(red), g(green), g(blue)]
|
50
|
+
end
|
51
|
+
|
52
|
+
def set_color_space_fill(name)
|
53
|
+
@stream << "/#{name} cs\n"
|
54
|
+
end
|
55
|
+
|
56
|
+
def set_color_space_stroke(name)
|
57
|
+
@stream << "/#{name} CS\n"
|
58
|
+
end
|
59
|
+
|
60
|
+
def set_color_fill(colors)
|
61
|
+
@stream << "%s sc\n" % colors.map { |c| g(c) }.join(' ')
|
62
|
+
end
|
63
|
+
|
64
|
+
def set_color_stroke(colors)
|
65
|
+
@stream << "%s SC\n" % colors.map { |c| g(c) }.join(' ')
|
66
|
+
end
|
67
|
+
|
68
|
+
# xxx scn, SCN: patterns and separations
|
69
|
+
def set_color_rendering_intent(intent)
|
70
|
+
@stream << "/#{intent} ri\n"
|
71
|
+
end
|
72
|
+
|
73
|
+
def x_object(name)
|
74
|
+
@stream << "/#{name} Do\n"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class GraphWriter < BaseWriter # :nodoc:
|
79
|
+
def initialize(stream)
|
80
|
+
@stream = stream
|
81
|
+
end
|
82
|
+
|
83
|
+
def save_graphics_state
|
84
|
+
@stream << "q\n"
|
85
|
+
end
|
86
|
+
|
87
|
+
def restore_graphics_state
|
88
|
+
@stream << "Q\n"
|
89
|
+
end
|
90
|
+
|
91
|
+
def concat_matrix(a, b, c, d, x, y)
|
92
|
+
@stream << "%s %s %s %s %s %s cm\n" % [g(a), g(b), g(c), g(d), g(x), g(y)]
|
93
|
+
end
|
94
|
+
|
95
|
+
def set_flatness(flatness)
|
96
|
+
@stream << "%d i\n" % flatness
|
97
|
+
end
|
98
|
+
|
99
|
+
def set_line_cap_style(line_cap_style)
|
100
|
+
@stream << "%d J\n" % line_cap_style
|
101
|
+
end
|
102
|
+
|
103
|
+
def set_line_dash_pattern(line_dash_pattern)
|
104
|
+
@stream << "%s d\n" % line_dash_pattern
|
105
|
+
end
|
106
|
+
|
107
|
+
def set_line_join_style(line_join_style)
|
108
|
+
@stream << "%d j\n" % line_join_style
|
109
|
+
end
|
110
|
+
|
111
|
+
def set_line_width(line_width)
|
112
|
+
@stream << "%s w\n" % g(line_width)
|
113
|
+
end
|
114
|
+
|
115
|
+
def set_miter_limit(miter_limit)
|
116
|
+
@stream << "%s M\n" % g(miter_limit)
|
117
|
+
end
|
118
|
+
|
119
|
+
def move_to(x, y)
|
120
|
+
@stream << "%s %s m\n" % [g(x), g(y)]
|
121
|
+
end
|
122
|
+
|
123
|
+
def line_to(x, y)
|
124
|
+
@stream << "%s %s l\n" % [g(x), g(y)]
|
125
|
+
end
|
126
|
+
|
127
|
+
def curve_to(x1, y1, x2, y2, x3, y3)
|
128
|
+
@stream << "%s %s %s %s %s %s c\n" % [g(x1), g(y1), g(x2), g(y2), g(x3), g(y3)]
|
129
|
+
end
|
130
|
+
|
131
|
+
def rectangle(x, y, width, height)
|
132
|
+
@stream << "%s %s %s %s re\n" % [g(x), g(y), g(width), g(height)]
|
133
|
+
end
|
134
|
+
|
135
|
+
def close_path
|
136
|
+
@stream << "h\n"
|
137
|
+
end
|
138
|
+
|
139
|
+
def new_path
|
140
|
+
@stream << "n\n"
|
141
|
+
end
|
142
|
+
|
143
|
+
def stroke
|
144
|
+
@stream << "S\n"
|
145
|
+
end
|
146
|
+
|
147
|
+
def close_path_and_stroke
|
148
|
+
@stream << "s\n"
|
149
|
+
end
|
150
|
+
|
151
|
+
def fill
|
152
|
+
@stream << "f\n"
|
153
|
+
end
|
154
|
+
|
155
|
+
def eo_fill
|
156
|
+
@stream << "f*\n"
|
157
|
+
end
|
158
|
+
|
159
|
+
def fill_and_stroke
|
160
|
+
@stream << "B\n"
|
161
|
+
end
|
162
|
+
|
163
|
+
def close_path_fill_and_stroke
|
164
|
+
@stream << "b\n"
|
165
|
+
end
|
166
|
+
|
167
|
+
def eo_fill_and_stroke
|
168
|
+
@stream << "B*\n"
|
169
|
+
end
|
170
|
+
|
171
|
+
def close_path_eo_fill_and_stroke
|
172
|
+
@stream << "b*\n"
|
173
|
+
end
|
174
|
+
|
175
|
+
# use nonzero winding number rule
|
176
|
+
def clip
|
177
|
+
@stream << "W\n"
|
178
|
+
end
|
179
|
+
|
180
|
+
# use even-odd rule
|
181
|
+
def eo_clip
|
182
|
+
@stream << "W*\n"
|
183
|
+
end
|
184
|
+
|
185
|
+
def make_line_dash_pattern(pattern, phase)
|
186
|
+
"[%s] %d" % [pattern.join(' '), phase]
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
class TextWriter < BaseWriter # :nodoc:
|
191
|
+
def initialize(stream)
|
192
|
+
@stream = stream
|
193
|
+
end
|
194
|
+
|
195
|
+
def open
|
196
|
+
@stream << "BT\n"
|
197
|
+
end
|
198
|
+
|
199
|
+
def close
|
200
|
+
@stream << "ET\n"
|
201
|
+
end
|
202
|
+
|
203
|
+
def set_char_spacing(char_space)
|
204
|
+
@stream << "%s Tc\n" % g(char_space)
|
205
|
+
end
|
206
|
+
|
207
|
+
def set_word_spacing(word_space)
|
208
|
+
@stream << "%s Tw\n" % g(word_space)
|
209
|
+
end
|
210
|
+
|
211
|
+
def set_horiz_scaling(scale)
|
212
|
+
@stream << "%s Tz\n" % g(scale)
|
213
|
+
end
|
214
|
+
|
215
|
+
def set_leading(leading)
|
216
|
+
@stream << "%s TL\n" % g(leading)
|
217
|
+
end
|
218
|
+
|
219
|
+
def set_font_and_size(font_name, size)
|
220
|
+
@stream << "/%s %s Tf\n" % [font_name, g(size)]
|
221
|
+
end
|
222
|
+
|
223
|
+
def set_rendering_mode(render)
|
224
|
+
@stream << "%d Tr\n" % render
|
225
|
+
end
|
226
|
+
|
227
|
+
def set_rise(rise)
|
228
|
+
@stream << "%s Ts\n" % g(rise)
|
229
|
+
end
|
230
|
+
|
231
|
+
def move_by(tx, ty)
|
232
|
+
@stream << "%s %s Td\n" % [g(tx), g(ty)]
|
233
|
+
end
|
234
|
+
|
235
|
+
def move_by_and_set_leading(tx, ty)
|
236
|
+
@stream << "%s %s TD\n" % [g(tx), g(ty)]
|
237
|
+
end
|
238
|
+
|
239
|
+
def set_matrix(a, b, c, d, x, y)
|
240
|
+
@stream << "%s %s %s %s %s %s Tm\n" % [g(a), g(b), g(c), g(d), g(x), g(y)]
|
241
|
+
end
|
242
|
+
|
243
|
+
def next_line
|
244
|
+
@stream << "T*\n"
|
245
|
+
end
|
246
|
+
|
247
|
+
def show(s)
|
248
|
+
@stream << "(%s) Tj\n" % PdfObjects::PdfString.escape(s)
|
249
|
+
end
|
250
|
+
|
251
|
+
def show_wide(ws)
|
252
|
+
@stream << "(%s) Tj\n" % PdfObjects::PdfString.escape_wide(ws)
|
253
|
+
end
|
254
|
+
|
255
|
+
def next_line_show(s)
|
256
|
+
@stream << "(%s) '" % PdfObjects::PdfString.escape(s)
|
257
|
+
end
|
258
|
+
|
259
|
+
def set_spacing_next_line_show(char_space, word_space, s)
|
260
|
+
@stream << "%s %s (%s) \"\n" % [g(char_space), g(word_space), PdfObjects::PdfString.escape(s)]
|
261
|
+
end
|
262
|
+
|
263
|
+
def show_with_dispacements(elements)
|
264
|
+
@stream << "%sTJ\n" % elements
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
data/lib/epdft.rb
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: ASCII-8BIT
|
3
|
+
#
|
4
|
+
# Created by Brent Rowland on 2007-11-27.
|
5
|
+
# Copyright (c) 2007, Eidetic Software. All rights reserved.
|
6
|
+
|
7
|
+
module EideticPDF
|
8
|
+
module TextLine # :nodoc:
|
9
|
+
def height
|
10
|
+
@height ||= map { |p| 0.001 * p.font.height * p.font.size }.max
|
11
|
+
end
|
12
|
+
|
13
|
+
def ascent
|
14
|
+
@ascent ||= map { |p| 0.001 * p.font.ascent * p.font.size }.max
|
15
|
+
end
|
16
|
+
|
17
|
+
def descent
|
18
|
+
@descent ||= map { |p| 0.001 * p.font.descent * p.font.size }.min
|
19
|
+
end
|
20
|
+
|
21
|
+
def width
|
22
|
+
@width ||= inject(0) { |total, p| total + p.width }
|
23
|
+
end
|
24
|
+
|
25
|
+
def chars
|
26
|
+
@chars ||= inject(0) { |total, p| total + p.chars }
|
27
|
+
end
|
28
|
+
|
29
|
+
def tokens
|
30
|
+
@tokens ||= inject(0) { |total, p| total + p.tokens }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module PdfText # :nodoc: all
|
35
|
+
class TextPiece
|
36
|
+
attr_accessor :text, :width, :font, :color, :underline, :chars, :tokens
|
37
|
+
|
38
|
+
def initialize(text, width, font, color, underline, chars, tokens)
|
39
|
+
@text, @width, @font, @color, @underline, @chars, @tokens = text, width, font, color, underline, chars, tokens
|
40
|
+
end
|
41
|
+
|
42
|
+
def initialize_copy(other)
|
43
|
+
@text = @text.clone
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class RichText
|
48
|
+
TOKEN_RE = /\n|\t|[ ]|[\S]+-+|[\S]+/
|
49
|
+
attr_reader :words
|
50
|
+
|
51
|
+
def initialize(text=nil, font=nil, options={})
|
52
|
+
@words = []
|
53
|
+
add(text, font, options) unless text.nil?
|
54
|
+
end
|
55
|
+
|
56
|
+
def initialize_copy(other)
|
57
|
+
@words = @words.map { |word| word.clone }
|
58
|
+
end
|
59
|
+
|
60
|
+
def add(text, font, options={})
|
61
|
+
fsize = font.size * 0.001
|
62
|
+
char_spacing, word_spacing = options[:char_spacing] || 0, options[:word_spacing] || 0
|
63
|
+
color, underlined = options[:color] || 0, options[:underline] || false
|
64
|
+
words = text.scan(TOKEN_RE).map do |token|
|
65
|
+
width = 0.0
|
66
|
+
token.each_byte do |b|
|
67
|
+
width += fsize * font.widths[b] + char_spacing
|
68
|
+
width += word_spacing if b == 32 # space
|
69
|
+
end
|
70
|
+
TextPiece.new(token, width, font, color, underlined, token.length, 1)
|
71
|
+
end
|
72
|
+
@words.concat(words)
|
73
|
+
end
|
74
|
+
|
75
|
+
# merge pieces with identical characteristics
|
76
|
+
def merge(text_pieces)
|
77
|
+
while !text_pieces.empty? and text_pieces.last.text == ' '
|
78
|
+
text_pieces.pop
|
79
|
+
end
|
80
|
+
text_pieces.inject([]) do |pieces, piece|
|
81
|
+
if pieces.empty? or [pieces.last.font, pieces.last.color, pieces.last.underline] != [piece.font, piece.color, piece.underline]
|
82
|
+
pieces << piece
|
83
|
+
else
|
84
|
+
pieces.last.text << piece.text
|
85
|
+
pieces.last.width += piece.width
|
86
|
+
pieces.last.chars += piece.chars
|
87
|
+
pieces.last.tokens += piece.tokens
|
88
|
+
end
|
89
|
+
pieces
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def next(width)
|
94
|
+
# remove leading spaces
|
95
|
+
@words.shift while !@words.empty? and @words.first.text == ' '
|
96
|
+
return nil if @words.empty?
|
97
|
+
# measure how many words will fit
|
98
|
+
i, phrase_width = 0, 0.0
|
99
|
+
while i < @words.size and phrase_width + @words[i].width < width
|
100
|
+
phrase_width += @words[i].width
|
101
|
+
i += 1
|
102
|
+
break if @words[i-1].text == "\n"
|
103
|
+
end
|
104
|
+
merge(@words.slice!(0, [i,1].max)).extend(TextLine)
|
105
|
+
end
|
106
|
+
|
107
|
+
def lines(width)
|
108
|
+
rich_text = self.clone
|
109
|
+
result = []
|
110
|
+
while line = rich_text.next(width)
|
111
|
+
result << line
|
112
|
+
end
|
113
|
+
result
|
114
|
+
end
|
115
|
+
|
116
|
+
def empty?
|
117
|
+
@words.empty?
|
118
|
+
end
|
119
|
+
|
120
|
+
def ascent(width=nil)
|
121
|
+
return 0 if @words.empty?
|
122
|
+
if width.nil?
|
123
|
+
@words.clone.extend(TextLine).ascent
|
124
|
+
else
|
125
|
+
self.clone.next(width).ascent
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def height(width=nil)
|
130
|
+
return 0 if @words.empty?
|
131
|
+
if width.nil?
|
132
|
+
@words.clone.extend(TextLine).height
|
133
|
+
# f = @words.first.font
|
134
|
+
# 0.001 * f.height * f.size
|
135
|
+
else
|
136
|
+
lines(width).inject(0) { |total, line| total + line.height }
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def width(max_width)
|
141
|
+
lines(max_width).map { |line| line.width }.max
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|