eideticpdf 0.9.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|