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/epdfpw.rb
ADDED
@@ -0,0 +1,1749 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: ASCII-8BIT
|
3
|
+
#
|
4
|
+
# Created by Brent Rowland on 2007-07-13.
|
5
|
+
# Copyright (c) 2007, Eidetic Software. All rights reserved.
|
6
|
+
|
7
|
+
require 'epdfo'
|
8
|
+
require 'epdfsw'
|
9
|
+
require 'epdfk'
|
10
|
+
require 'epdft'
|
11
|
+
require 'epdfafm'
|
12
|
+
require 'epdftt'
|
13
|
+
require 'epdfs'
|
14
|
+
|
15
|
+
module EideticPDF
|
16
|
+
Font = Struct.new(:name, :size, :style, :color, :encoding, :sub_type, :widths, :ascent, :descent, :height,
|
17
|
+
:underline_position, :underline_thickness)
|
18
|
+
Location = Struct.new(:x, :y)
|
19
|
+
Signs = Struct.new(:x, :y)
|
20
|
+
Bullet = Struct.new(:name, :width, :proc)
|
21
|
+
|
22
|
+
SIGNS = [ Signs.new(1, -1), Signs.new(-1, -1), Signs.new(-1, 1), Signs.new(1, 1) ]
|
23
|
+
UNIT_CONVERSION = { :pt => 1, :in => 72, :cm => 28.35 }
|
24
|
+
LINE_PATTERNS = { :solid => [], :dotted => [1, 2], :dashed => [4, 2] }
|
25
|
+
LINE_CAP_STYLES = [:butt_cap, :round_cap, :projecting_square_cap].freeze
|
26
|
+
IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0].freeze
|
27
|
+
|
28
|
+
class PageStyle # :nodoc:
|
29
|
+
attr_reader :page_size, :crop_size, :orientation, :landscape, :rotate
|
30
|
+
|
31
|
+
PORTRAIT = 0
|
32
|
+
LANDSCAPE = 270
|
33
|
+
|
34
|
+
def initialize(options={})
|
35
|
+
page_size = options[:page_size] || :letter
|
36
|
+
crop_size = options[:crop_size] || page_size
|
37
|
+
@orientation = options[:orientation] || :portrait
|
38
|
+
@page_size = make_size_rectangle(page_size, @orientation)
|
39
|
+
@crop_size = make_size_rectangle(crop_size, @orientation)
|
40
|
+
@landscape = (@orientation == :landscape)
|
41
|
+
@rotate = ROTATIONS[options[:rotate] || :portrait]
|
42
|
+
end
|
43
|
+
|
44
|
+
SIZES = {
|
45
|
+
:letter => [612, 792].freeze,
|
46
|
+
:legal => [612, 1008].freeze,
|
47
|
+
:A4 => [595, 842].freeze,
|
48
|
+
:B5 => [499, 708].freeze,
|
49
|
+
:C5 => [459, 649].freeze
|
50
|
+
}
|
51
|
+
ROTATIONS = { :portrait => PORTRAIT, :landscape => LANDSCAPE }.freeze
|
52
|
+
|
53
|
+
protected
|
54
|
+
def make_size_rectangle(size, orientation)
|
55
|
+
w, h = SIZES[size]
|
56
|
+
w, h = h, w if orientation == :landscape
|
57
|
+
PdfObjects::Rectangle.new(0, 0, w, h)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class PageWriter # :nodoc:
|
62
|
+
include JpegInfo
|
63
|
+
|
64
|
+
private
|
65
|
+
def iconv_encoding(encoding)
|
66
|
+
case encoding
|
67
|
+
when 'WinAnsiEncoding' then 'CP1252'
|
68
|
+
else encoding
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def pdf_encoding(encoding, font_name)
|
73
|
+
return 'StandardEncoding' if ['Symbol','ZapfDingbats'].include?(font_name)
|
74
|
+
case encoding.upcase
|
75
|
+
when 'CP1252' then 'WinAnsiEncoding'
|
76
|
+
else encoding
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def to_points(units, measurement)
|
81
|
+
UNIT_CONVERSION[units] * measurement
|
82
|
+
end
|
83
|
+
|
84
|
+
def from_points(units, measurement)
|
85
|
+
measurement.quo(UNIT_CONVERSION[units])
|
86
|
+
end
|
87
|
+
|
88
|
+
def make_loc(x, y)
|
89
|
+
Location.new(x, y)
|
90
|
+
end
|
91
|
+
|
92
|
+
def translate(x, y)
|
93
|
+
Location.new(x, page_height - y)
|
94
|
+
end
|
95
|
+
|
96
|
+
def translate_p(p)
|
97
|
+
Location.new(p.x, page_height - p.y)
|
98
|
+
end
|
99
|
+
|
100
|
+
def convert_units(loc, from_units, to_units)
|
101
|
+
Location.new(
|
102
|
+
loc.x * UNIT_CONVERSION[from_units].quo(UNIT_CONVERSION[to_units]),
|
103
|
+
loc.y * UNIT_CONVERSION[from_units].quo(UNIT_CONVERSION[to_units]))
|
104
|
+
end
|
105
|
+
|
106
|
+
def get_quadrant_bezier_points(quadrant, x, y, rx, ry=nil)
|
107
|
+
ry = rx if ry.nil?
|
108
|
+
a = 4.0 / 3.0 * (Math.sqrt(2) - 1.0)
|
109
|
+
bp = []
|
110
|
+
if quadrant.odd? # quadrant is odd
|
111
|
+
# (1,0)
|
112
|
+
bp << make_loc(x + (rx * SIGNS[quadrant - 1].x), y)
|
113
|
+
# (1,a)
|
114
|
+
bp << make_loc(bp[0].x, y + (a * ry * SIGNS[quadrant - 1].y))
|
115
|
+
# (a,1)
|
116
|
+
bp << make_loc(x + (a * rx * SIGNS[quadrant - 1].x), y + (ry * SIGNS[quadrant - 1].y))
|
117
|
+
# (0,1)
|
118
|
+
bp << make_loc(x, bp[2].y)
|
119
|
+
else # quadrant is even
|
120
|
+
# (0,1)
|
121
|
+
bp << make_loc(x, y + (ry * SIGNS[quadrant - 1].y))
|
122
|
+
# (a,1)
|
123
|
+
bp << make_loc(x + (a * rx * SIGNS[quadrant - 1].x), bp[0].y)
|
124
|
+
# (1,a)
|
125
|
+
bp << make_loc(x + (rx * SIGNS[quadrant - 1].x), y + (a * ry * SIGNS[quadrant - 1].y))
|
126
|
+
# (1,0)
|
127
|
+
bp << make_loc(bp[2].x, y)
|
128
|
+
end
|
129
|
+
bp
|
130
|
+
end
|
131
|
+
|
132
|
+
def rotate_xy_coordinate(x, y, angle)
|
133
|
+
theta = angle.degrees
|
134
|
+
r_cos = Math::cos(theta)
|
135
|
+
r_sin = Math::sin(theta)
|
136
|
+
x_rot = (r_cos * x) - (r_sin * y)
|
137
|
+
y_rot = (r_sin * x) + (r_cos * y)
|
138
|
+
[x_rot, y_rot]
|
139
|
+
end
|
140
|
+
|
141
|
+
def rotate_point(loc, angle)
|
142
|
+
x, y = rotate_xy_coordinate(loc.x, loc.y, angle)
|
143
|
+
make_loc(x, y)
|
144
|
+
end
|
145
|
+
|
146
|
+
def add_vector(point, angle, distance)
|
147
|
+
theta = angle.degrees
|
148
|
+
Location.new(point.x + Math::cos(theta) * distance, point.y + Math::sin(theta) * distance)
|
149
|
+
end
|
150
|
+
|
151
|
+
def rotate_points(mid, points, angle)
|
152
|
+
theta = angle.degrees
|
153
|
+
r_cos = Math::cos(theta)
|
154
|
+
r_sin = Math::sin(theta)
|
155
|
+
points.map do |p|
|
156
|
+
x, y = p.x - mid.x, p.y - mid.y
|
157
|
+
x_rot = (r_cos * x) - (r_sin * y)
|
158
|
+
y_rot = (r_sin * x) + (r_cos * y)
|
159
|
+
make_loc(x_rot + mid.x, y_rot + mid.y)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def calc_arc_small(r, mid_theta, half_angle, ccwcw)
|
164
|
+
half_theta = half_angle.abs.degrees
|
165
|
+
v_cos = Math::cos(half_theta)
|
166
|
+
v_sin = Math::sin(half_theta)
|
167
|
+
|
168
|
+
x0 = r * v_cos
|
169
|
+
y0 = -ccwcw * r * v_sin
|
170
|
+
x1 = r * (4.0 - v_cos) / 3.0
|
171
|
+
x2 = x1
|
172
|
+
y1 = r * ccwcw * (1.0 - v_cos) * (v_cos - 3.0) / (3.0 * v_sin)
|
173
|
+
y2 = -y1
|
174
|
+
x3 = r * v_cos
|
175
|
+
y3 = ccwcw * r * v_sin
|
176
|
+
|
177
|
+
x0, y0 = rotate_xy_coordinate(x0, y0, mid_theta)
|
178
|
+
x1, y1 = rotate_xy_coordinate(x1, y1, mid_theta)
|
179
|
+
x2, y2 = rotate_xy_coordinate(x2, y2, mid_theta)
|
180
|
+
x3, y3 = rotate_xy_coordinate(x3, y3, mid_theta)
|
181
|
+
|
182
|
+
[x0, y0, x1, y1, x2, y2, x3, y3]
|
183
|
+
end
|
184
|
+
|
185
|
+
def points_for_arc_small(x, y, r, mid_theta, half_angle, ccwcw)
|
186
|
+
x0, y0, x1, y1, x2, y2, x3, y3 = calc_arc_small(r, mid_theta, half_angle, ccwcw)
|
187
|
+
[make_loc(x+x0, y-y0), make_loc(x+x1, y-y1), make_loc(x+x2, y-y2), make_loc(x+x3, y-y3)]
|
188
|
+
end
|
189
|
+
|
190
|
+
def arc_small(x, y, r, mid_theta, half_angle, ccwcw, move_to0)
|
191
|
+
x0, y0, x1, y1, x2, y2, x3, y3 = calc_arc_small(r, mid_theta, half_angle, ccwcw)
|
192
|
+
line_to(x+x0, y-y0) unless move_to0
|
193
|
+
curve(x+x0, y-y0, x+x1, y-y1, x+x2, y-y2, x+x3, y-y3)
|
194
|
+
end
|
195
|
+
|
196
|
+
def set_text_angle(angle, x, y)
|
197
|
+
theta = angle.degrees
|
198
|
+
v_cos = Math::cos(theta)
|
199
|
+
v_sin = Math::sin(theta)
|
200
|
+
@tw.set_matrix(v_cos, v_sin, -v_sin, v_cos, to_points(@units, x), to_points(@units, y))
|
201
|
+
@text_angle = angle
|
202
|
+
end
|
203
|
+
|
204
|
+
def rgb_from_color(color)
|
205
|
+
color = named_colors[color] || 0 if color.respond_to? :to_str
|
206
|
+
color ||= 0
|
207
|
+
b = color & 0xFF
|
208
|
+
g = (color >> 8) & 0xFF
|
209
|
+
r = (color >> 16) & 0xFF
|
210
|
+
[r, g, b]
|
211
|
+
end
|
212
|
+
|
213
|
+
def color_from_rgb(r, g, b)
|
214
|
+
(r << 16) | (g << 8) | b
|
215
|
+
end
|
216
|
+
protected
|
217
|
+
def start_text
|
218
|
+
raise Exception.new("Already in text.") if @in_text
|
219
|
+
raise Exception.new("Not in page.") unless @doc.in_page
|
220
|
+
end_graph if @in_graph
|
221
|
+
@last_loc = Location.new(0, 0)
|
222
|
+
@in_text = true
|
223
|
+
@tw = TextWriter.new(@stream)
|
224
|
+
@tw.open
|
225
|
+
@tw
|
226
|
+
end
|
227
|
+
|
228
|
+
def end_text
|
229
|
+
raise Exception.new("Not in text.") unless @in_text
|
230
|
+
@tw.close
|
231
|
+
@in_text = false
|
232
|
+
end
|
233
|
+
|
234
|
+
def start_graph
|
235
|
+
raise Exception.new("Already in graph") if @in_graph
|
236
|
+
end_text if @in_text
|
237
|
+
@last_loc = Location.new(0, 0)
|
238
|
+
@in_graph = true
|
239
|
+
@gw = GraphWriter.new(@stream)
|
240
|
+
end
|
241
|
+
|
242
|
+
def end_path
|
243
|
+
gw.stroke if @auto_path
|
244
|
+
@in_path = false
|
245
|
+
end
|
246
|
+
|
247
|
+
def end_graph
|
248
|
+
raise Exception.new("Not in graph") unless @in_graph
|
249
|
+
end_path if @in_path
|
250
|
+
@gw = nil
|
251
|
+
@in_graph = false
|
252
|
+
end
|
253
|
+
|
254
|
+
def start_misc
|
255
|
+
raise Exception.new("Already in misc") if @in_misc
|
256
|
+
@in_misc = true
|
257
|
+
@mw = MiscWriter.new(@stream)
|
258
|
+
end
|
259
|
+
|
260
|
+
def end_misc
|
261
|
+
raise Exception.new("Not in misc") unless @in_misc
|
262
|
+
@mw = nil
|
263
|
+
@in_misc = false
|
264
|
+
end
|
265
|
+
|
266
|
+
def tw
|
267
|
+
@tw ||= start_text
|
268
|
+
end
|
269
|
+
|
270
|
+
def gw
|
271
|
+
@gw ||= start_graph
|
272
|
+
end
|
273
|
+
|
274
|
+
def mw
|
275
|
+
@mw ||= start_misc
|
276
|
+
end
|
277
|
+
|
278
|
+
def end_margins
|
279
|
+
end_path if @in_path
|
280
|
+
gw.restore_graphics_state
|
281
|
+
end
|
282
|
+
|
283
|
+
def end_sub_page
|
284
|
+
end_path if @in_path
|
285
|
+
gw.restore_graphics_state
|
286
|
+
end
|
287
|
+
|
288
|
+
def sub_orientation(pages_across, pages_down)
|
289
|
+
@page_width / pages_across > @page_height / pages_down ? :landscape : :portrait
|
290
|
+
end
|
291
|
+
|
292
|
+
# font methods
|
293
|
+
def set_default_font
|
294
|
+
font(@default_font[:name], @default_font[:size], @default_font)
|
295
|
+
@font
|
296
|
+
end
|
297
|
+
|
298
|
+
def check_set_font
|
299
|
+
set_default_font if @font.nil?
|
300
|
+
if (@last_page_font != @page_font) or (@last_font != @font)
|
301
|
+
@tw.set_font_and_size(@page_font, @font.size)
|
302
|
+
check_set_v_text_align(true)
|
303
|
+
@last_page_font = @page_font
|
304
|
+
@last_font = @font
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
def check_set_v_text_align(force=false)
|
309
|
+
if force or @last_v_text_align != @v_text_align
|
310
|
+
@v_text_align_pts = case @v_text_align
|
311
|
+
when :above then -@font.height * 0.001 * @font.size
|
312
|
+
when :top then -@font.ascent * 0.001 * @font.size
|
313
|
+
when :middle then -@font.ascent * 0.001 * @font.size / 2.0
|
314
|
+
when :below then -@font.descent * 0.001 * @font.size
|
315
|
+
else 0.0 # :base
|
316
|
+
end
|
317
|
+
@tw.set_rise(@v_text_align_pts)
|
318
|
+
@last_v_text_align = @v_text_align
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
def check_set_spacing
|
323
|
+
unless @word_spacing == @last_word_spacing
|
324
|
+
tw.set_word_spacing(@word_spacing)
|
325
|
+
@last_word_spacing = @word_spacing
|
326
|
+
end
|
327
|
+
unless @char_spacing == @last_char_spacing
|
328
|
+
tw.set_char_spacing(@char_spacing)
|
329
|
+
@last_char_spacing = @char_spacing
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
def check_set_scale
|
334
|
+
unless @scale == @last_scale
|
335
|
+
tw.set_horiz_scaling(@scale * 100)
|
336
|
+
@last_scale = @scale
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
def text_rendering_mode(options)
|
341
|
+
if options[:fill] and options[:stroke]
|
342
|
+
@text_rendering_mode = 2
|
343
|
+
elsif options[:stroke]
|
344
|
+
@text_rendering_mode = 1
|
345
|
+
elsif options[:fill]
|
346
|
+
@text_rendering_mode = 0
|
347
|
+
elsif options[:invisible]
|
348
|
+
@text_rendering_mode = 3 # Why is this an option in PDF?
|
349
|
+
else
|
350
|
+
@text_rendering_mode = 0
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
def text_clipping_mode(options)
|
355
|
+
if options[:fill] and options[:stroke]
|
356
|
+
@text_rendering_mode = 6
|
357
|
+
elsif options[:stroke]
|
358
|
+
@text_rendering_mode = 5
|
359
|
+
elsif options[:fill]
|
360
|
+
@text_rendering_mode = 4
|
361
|
+
else
|
362
|
+
@text_rendering_mode = 7
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
def check_set_text_rendering_mode
|
367
|
+
unless @text_rendering_mode == @last_text_rendering_mode
|
368
|
+
tw.set_rendering_mode(@text_rendering_mode)
|
369
|
+
@last_text_rendering_mode = @text_rendering_mode
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
# color methods
|
374
|
+
def check_set_line_color
|
375
|
+
unless @line_color == @last_line_color
|
376
|
+
r, g, b = rgb_from_color(@line_color)
|
377
|
+
if @in_path and @auto_path
|
378
|
+
gw.stroke
|
379
|
+
@in_path = false
|
380
|
+
end
|
381
|
+
if @in_misc
|
382
|
+
mw.set_rgb_color_stroke(r / 255.0, g / 255.0, b / 255.0)
|
383
|
+
@last_line_color = @line_color
|
384
|
+
end
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
def check_set_fill_color
|
389
|
+
unless @fill_color == @last_fill_color
|
390
|
+
r, g, b = rgb_from_color(@fill_color)
|
391
|
+
if @in_path and @auto_path
|
392
|
+
gw.stroke
|
393
|
+
@in_path = false
|
394
|
+
end
|
395
|
+
if @in_misc
|
396
|
+
mw.set_rgb_color_fill(r / 255.0, g / 255.0, b / 255.0)
|
397
|
+
@last_fill_color = @fill_color
|
398
|
+
end
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
def check_set_font_color
|
403
|
+
unless @font_color == @last_fill_color
|
404
|
+
r, g, b = rgb_from_color(@font_color)
|
405
|
+
if @in_path and @auto_path
|
406
|
+
gw.stroke
|
407
|
+
@in_path = false
|
408
|
+
end
|
409
|
+
if @in_misc
|
410
|
+
mw.set_rgb_color_fill(r / 255.0, g / 255.0, b / 255.0)
|
411
|
+
@last_fill_color = @font_color
|
412
|
+
end
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
def check_set_line_dash_pattern
|
417
|
+
unless [@line_dash_pattern, @line_cap_style] == [@last_line_dash_pattern, @last_line_cap_style]
|
418
|
+
if @in_path and @auto_path
|
419
|
+
gw.stroke
|
420
|
+
@in_path = false
|
421
|
+
end
|
422
|
+
|
423
|
+
if @line_dash_pattern.is_a?(Symbol)
|
424
|
+
dashes = (LINE_PATTERNS[@line_dash_pattern] || []).map { |p| p * @line_width.round }
|
425
|
+
pattern = gw.make_line_dash_pattern(dashes, 0)
|
426
|
+
else
|
427
|
+
pattern = @line_dash_pattern.to_s
|
428
|
+
end
|
429
|
+
|
430
|
+
gw.set_line_dash_pattern(pattern)
|
431
|
+
@last_line_dash_pattern = @line_dash_pattern
|
432
|
+
|
433
|
+
gw.set_line_cap_style(LINE_CAP_STYLES.index(@line_cap_style) || 0)
|
434
|
+
@last_line_cap_style = @line_cap_style
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
def check_set_line_width
|
439
|
+
unless @line_width == @last_line_width
|
440
|
+
if @in_path and @auto_path
|
441
|
+
gw.stroke
|
442
|
+
@in_path = false
|
443
|
+
end
|
444
|
+
|
445
|
+
gw.set_line_width(@line_width)
|
446
|
+
@last_line_width = @line_width
|
447
|
+
@last_line_dash_pattern = nil if @last_line_dash_pattern.is_a?(Symbol)
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
451
|
+
def check_set(*options)
|
452
|
+
check_set_line_color if options.include?(:line_color)
|
453
|
+
check_set_fill_color if options.include?(:fill_color)
|
454
|
+
check_set_line_width if options.include?(:line_width)
|
455
|
+
check_set_line_dash_pattern if options.include?(:line_dash_pattern)
|
456
|
+
check_set_font if options.include?(:font)
|
457
|
+
check_set_font_color if options.include?(:font_color)
|
458
|
+
check_set_v_text_align if options.include?(:v_text_align)
|
459
|
+
check_set_spacing if options.include?(:spacing)
|
460
|
+
check_set_scale if options.include?(:scale)
|
461
|
+
check_set_text_rendering_mode if options.include?(:text_rendering_mode)
|
462
|
+
end
|
463
|
+
|
464
|
+
def line_colors
|
465
|
+
@line_colors ||= ColorStack.new(self, :line_color)
|
466
|
+
end
|
467
|
+
|
468
|
+
def fill_colors
|
469
|
+
@fill_colors ||= ColorStack.new(self, :fill_color)
|
470
|
+
end
|
471
|
+
|
472
|
+
def auto_stroke_and_fill(options)
|
473
|
+
if @auto_path
|
474
|
+
gw.clip if options[:clip]
|
475
|
+
if (options[:stroke] and options[:fill])
|
476
|
+
gw.fill_and_stroke
|
477
|
+
elsif options[:stroke] then
|
478
|
+
gw.stroke
|
479
|
+
elsif options[:fill] then
|
480
|
+
gw.fill
|
481
|
+
else
|
482
|
+
gw.new_path
|
483
|
+
end
|
484
|
+
@in_path = false
|
485
|
+
end
|
486
|
+
end
|
487
|
+
|
488
|
+
# protected drawing methods
|
489
|
+
def draw_rounded_rectangle(x, y, width, height, options)
|
490
|
+
corners = options[:corners] || []
|
491
|
+
if corners.size == 1
|
492
|
+
xr1 = yr1 = xr2 = yr2 = xr3 = yr3 = xr4 = yr4 = corners[0]
|
493
|
+
elsif corners.size == 2
|
494
|
+
xr1 = yr1 = xr2 = yr2 = corners[0]
|
495
|
+
xr3 = yr3 = xr4 = yr4 = corners[1]
|
496
|
+
elsif corners.size == 4
|
497
|
+
xr1 = yr1 = corners[0]
|
498
|
+
xr2 = yr2 = corners[1]
|
499
|
+
xr3 = yr3 = corners[2]
|
500
|
+
xr4 = yr4 = corners[3]
|
501
|
+
elsif corners.size == 8
|
502
|
+
xr1, yr1, xr2, yr2, xr3, yr3, xr4, yr4 = corners
|
503
|
+
else
|
504
|
+
xr1 = yr1 = xr2 = yr2 = xr3 = yr3 = xr4 = yr4 = 0
|
505
|
+
end
|
506
|
+
|
507
|
+
q2p = get_quadrant_bezier_points(2, x + xr1, y + yr1, xr1, yr1)
|
508
|
+
q1p = get_quadrant_bezier_points(1, x + width - xr2, y + yr2, xr2, yr2)
|
509
|
+
q4p = get_quadrant_bezier_points(4, x + width - xr3, y + height - yr3, xr3, yr3)
|
510
|
+
q3p = get_quadrant_bezier_points(3, x + xr4, y + height - yr4, xr4, yr4)
|
511
|
+
|
512
|
+
qpa = [q1p, q2p, q3p, q4p]
|
513
|
+
if options[:reverse]
|
514
|
+
qpa.reverse!
|
515
|
+
qpa.each { |qp| qp.reverse! }
|
516
|
+
end
|
517
|
+
|
518
|
+
curve_points(qpa[0]); line_to(qpa[1][0].x, qpa[1][0].y)
|
519
|
+
curve_points(qpa[1]); line_to(qpa[2][0].x, qpa[2][0].y)
|
520
|
+
curve_points(qpa[2]); line_to(qpa[3][0].x, qpa[3][0].y)
|
521
|
+
curve_points(qpa[3]); line_to(qpa[0][0].x, qpa[0][0].y)
|
522
|
+
end
|
523
|
+
|
524
|
+
def draw_rectangle_path(x, y, width, height, options)
|
525
|
+
move_to(x, y)
|
526
|
+
if options[:reverse]
|
527
|
+
line_to(x, y + height)
|
528
|
+
line_to(x + width, y + height)
|
529
|
+
line_to(x + width, y)
|
530
|
+
else
|
531
|
+
line_to(x + width, y)
|
532
|
+
line_to(x + width, y + height)
|
533
|
+
line_to(x, y + height)
|
534
|
+
end
|
535
|
+
line_to(x, y)
|
536
|
+
end
|
537
|
+
|
538
|
+
def draw_underline(pos1, pos2, position, thickness, angle)
|
539
|
+
# position and thickness are in points
|
540
|
+
if @units != :pt
|
541
|
+
pos1, pos2 = convert_units(pos1, @units, :pt), convert_units(pos2, @units, :pt)
|
542
|
+
end
|
543
|
+
save_units = units(:pt)
|
544
|
+
save_line_width = line_width(thickness)
|
545
|
+
off_x, off_y = rotate_xy_coordinate(0, position - @v_text_align_pts, angle)
|
546
|
+
move_to(pos1.x - off_x, pos1.y + off_y)
|
547
|
+
line_to(pos2.x - off_x, pos2.y + off_y)
|
548
|
+
line_width(save_line_width)
|
549
|
+
units(save_units)
|
550
|
+
end
|
551
|
+
|
552
|
+
public
|
553
|
+
DEFAULT_FONT = { :name => 'Helvetica', :size => 12 }
|
554
|
+
|
555
|
+
attr_reader :doc, :page
|
556
|
+
attr_reader :stream, :annotations
|
557
|
+
attr_reader :auto_path
|
558
|
+
|
559
|
+
def initialize(doc, options)
|
560
|
+
# doc: PdfDocumentWriter
|
561
|
+
@doc = doc
|
562
|
+
@options = options
|
563
|
+
@page_style = PageStyle.new(options)
|
564
|
+
@units = options[:units] || :pt
|
565
|
+
@v_text_align = options[:v_text_align] || :top
|
566
|
+
@page_width = @page_style.page_size.x2
|
567
|
+
@page_height = @page_style.page_size.y2
|
568
|
+
if @page = options[:_page]
|
569
|
+
@reused_page = true
|
570
|
+
else
|
571
|
+
@page = PdfObjects::PdfPage.new(@doc.next_seq, 0, @doc.catalog.pages)
|
572
|
+
@page.media_box = @page_style.page_size.clone
|
573
|
+
@page.crop_box = @page_style.crop_size.clone
|
574
|
+
@page.rotate = @page_style.rotate
|
575
|
+
@page.resources = @doc.resources
|
576
|
+
@doc.file.body << @page
|
577
|
+
end
|
578
|
+
@stream = ''
|
579
|
+
@annotations = []
|
580
|
+
@char_spacing = @word_spacing = 0.0
|
581
|
+
@last_char_spacing = @last_word_spacing = 0.0
|
582
|
+
@last_scale = @scale = 1.0
|
583
|
+
@last_text_rendering_mode = @text_rendering_mode = 0
|
584
|
+
@default_font = options[:font] || DEFAULT_FONT
|
585
|
+
@font_color = @default_font[:color] || 0
|
586
|
+
@fill_color = options[:fill_color] || 0xFFFFFF
|
587
|
+
@line_color = options[:line_color] || 0
|
588
|
+
@line_height = options[:line_height] || 1.7
|
589
|
+
line_width(options[:line_width] || 1.0, :pt)
|
590
|
+
@text_angle = 0.0
|
591
|
+
@auto_path = true
|
592
|
+
@underline = false
|
593
|
+
start_misc
|
594
|
+
sub_page(*options[:sub_page] + Array(options[:unscaled])) if options[:sub_page]
|
595
|
+
margins(options[:margins] || 0)
|
596
|
+
text_encoding(options[:text_encoding])
|
597
|
+
@indent = 0
|
598
|
+
end
|
599
|
+
|
600
|
+
def close
|
601
|
+
end_margins unless @matrix.nil?
|
602
|
+
end_sub_page unless @sub_page.nil?
|
603
|
+
end_text if @in_text
|
604
|
+
end_graph if @in_graph
|
605
|
+
end_misc if @in_misc
|
606
|
+
pdf_stream = if @options[:compress]
|
607
|
+
require 'zlib'
|
608
|
+
zipper = Zlib::Deflate.new
|
609
|
+
zstream = zipper.deflate(@stream, Zlib::FINISH)
|
610
|
+
zpdf_stream = PdfObjects::PdfStream.new(@doc.next_seq, 0, zstream)
|
611
|
+
zpdf_stream.filter = 'FlateDecode'
|
612
|
+
zpdf_stream
|
613
|
+
else
|
614
|
+
PdfObjects::PdfStream.new(@doc.next_seq, 0, @stream)
|
615
|
+
end
|
616
|
+
@doc.file.body << pdf_stream
|
617
|
+
@page.annots = @annotations if @annotations.size.nonzero?
|
618
|
+
@page.contents << pdf_stream
|
619
|
+
@doc.catalog.pages.kids << @page unless @reused_page
|
620
|
+
@stream = nil
|
621
|
+
end
|
622
|
+
|
623
|
+
def closed?
|
624
|
+
@stream.nil?
|
625
|
+
end
|
626
|
+
|
627
|
+
def units(units=nil)
|
628
|
+
return @units if units.nil? or units == @units
|
629
|
+
@loc = convert_units(@loc, @units, units)
|
630
|
+
@last_loc = convert_units(@last_loc, @units, units) unless @last_loc.nil?
|
631
|
+
prev_units, @units = @units, units
|
632
|
+
prev_units
|
633
|
+
end
|
634
|
+
|
635
|
+
def margins(*margins)
|
636
|
+
@margins ||= [0] * 4
|
637
|
+
return @margins.map { |m| from_points(@units, m) } unless [4,2,1].include?(margins.size)
|
638
|
+
margins = margins.first if margins.first.is_a?(Array)
|
639
|
+
@margins = case margins.size
|
640
|
+
when 4 then margins
|
641
|
+
when 2 then margins * 2
|
642
|
+
when 1 then margins * 4
|
643
|
+
else @margins
|
644
|
+
end.map { |m| to_points(@units, m) }
|
645
|
+
@margin_top, @margin_right, @margin_bottom, @margin_left = @margins
|
646
|
+
if (@matrix || IDENTITY_MATRIX)[4..5] != [@margin_left, -@margin_top]
|
647
|
+
if @matrix.nil?
|
648
|
+
@matrix = IDENTITY_MATRIX.dup
|
649
|
+
else
|
650
|
+
gw.restore_graphics_state
|
651
|
+
end
|
652
|
+
@matrix[4..5] = [@margin_left, -@margin_top]
|
653
|
+
gw.save_graphics_state
|
654
|
+
gw.concat_matrix(*@matrix)
|
655
|
+
end
|
656
|
+
@canvas_width = @page_width - @margin_left - @margin_right
|
657
|
+
@canvas_height = @page_height - @margin_top - @margin_bottom
|
658
|
+
move_to(0, 0)
|
659
|
+
nil
|
660
|
+
end
|
661
|
+
|
662
|
+
def page_width
|
663
|
+
from_points(@units, @page_width)
|
664
|
+
end
|
665
|
+
|
666
|
+
def page_height
|
667
|
+
from_points(@units, @page_height)
|
668
|
+
end
|
669
|
+
|
670
|
+
def margin_top
|
671
|
+
from_points(@units, @margin_top)
|
672
|
+
end
|
673
|
+
|
674
|
+
def margin_right
|
675
|
+
from_points(@units, @margin_right)
|
676
|
+
end
|
677
|
+
|
678
|
+
def margin_bottom
|
679
|
+
from_points(@units, @margin_bottom)
|
680
|
+
end
|
681
|
+
|
682
|
+
def margin_left
|
683
|
+
from_points(@units, @margin_left)
|
684
|
+
end
|
685
|
+
|
686
|
+
def canvas_width
|
687
|
+
from_points(@units, @canvas_width)
|
688
|
+
end
|
689
|
+
|
690
|
+
def canvas_height
|
691
|
+
from_points(@units, @canvas_height)
|
692
|
+
end
|
693
|
+
|
694
|
+
def tabs(tabs=nil)
|
695
|
+
return @tabs if tabs.nil?
|
696
|
+
return @tabs = nil if tabs == false or tabs.empty?
|
697
|
+
tabs = tabs.split(',') if tabs.respond_to?(:to_str)
|
698
|
+
@tabs = tabs.map { |stop| stop.to_f }.select { |stop| stop > 0 }.sort
|
699
|
+
end
|
700
|
+
|
701
|
+
def tab(&block)
|
702
|
+
return if @tabs.nil?
|
703
|
+
p = pen_pos
|
704
|
+
x = @tabs.detect { |stop| stop > p.x }
|
705
|
+
if x.nil?
|
706
|
+
dy = block_given? ? yield : height
|
707
|
+
move_to(@tabs.first, p.y + dy)
|
708
|
+
else
|
709
|
+
move_to(x, p.y)
|
710
|
+
end
|
711
|
+
end
|
712
|
+
|
713
|
+
def vtabs(tabs=nil)
|
714
|
+
return @vtabs if tabs.nil?
|
715
|
+
return @vtabs = nil if tabs == false or tabs.empty?
|
716
|
+
tabs = tabs.split(',') if tabs.respond_to?(:to_str)
|
717
|
+
@vtabs = tabs.map { |stop| stop.to_f }.select { |stop| stop > 0 }.sort
|
718
|
+
end
|
719
|
+
|
720
|
+
def vtab(&block)
|
721
|
+
return if @vtabs.nil?
|
722
|
+
p = pen_pos
|
723
|
+
y = @vtabs.detect { |stop| stop > p.y }
|
724
|
+
if y.nil?
|
725
|
+
move_to(p.x + yield, @vtabs.first) if block_given?
|
726
|
+
else
|
727
|
+
move_to(p.x, y)
|
728
|
+
end
|
729
|
+
end
|
730
|
+
|
731
|
+
def indent(value=nil, absolute=false)
|
732
|
+
return @indent if value.nil?
|
733
|
+
prev_indent, @indent = @indent, absolute ? value : @indent + value
|
734
|
+
@loc.x = @indent
|
735
|
+
prev_indent
|
736
|
+
end
|
737
|
+
|
738
|
+
def sub_page(x, pages_across, y, pages_down, unscaled=false)
|
739
|
+
unless @matrix.nil?
|
740
|
+
gw.restore_graphics_state
|
741
|
+
@matrix = nil
|
742
|
+
end
|
743
|
+
gw.restore_graphics_state unless @sub_page.nil?
|
744
|
+
return unless x && pages_across && y && pages_down
|
745
|
+
if unscaled
|
746
|
+
@canvas_width = @page_width.quo(pages_across)
|
747
|
+
@canvas_height = @page_height.quo(pages_down)
|
748
|
+
@sub_page = IDENTITY_MATRIX.dup
|
749
|
+
@sub_page[4] = @canvas_width * x
|
750
|
+
@sub_page[5] = @canvas_height * (pages_down - 1 - y)
|
751
|
+
else
|
752
|
+
ps = PageStyle.new(@options.merge(:orientation => sub_orientation(pages_across, pages_down)))
|
753
|
+
width = ps.page_size.x2
|
754
|
+
height = ps.page_size.y2
|
755
|
+
|
756
|
+
ratio_w = @page_width.quo(pages_across * width)
|
757
|
+
ratio_h = @page_height.quo(pages_down * height)
|
758
|
+
ratio = [ratio_w, ratio_h].min
|
759
|
+
@sub_page = IDENTITY_MATRIX.dup
|
760
|
+
@sub_page[0] = ratio
|
761
|
+
@sub_page[3] = ratio
|
762
|
+
@sub_page[4] = @page_width.quo(pages_across) * x
|
763
|
+
@sub_page[5] = @page_height.quo(pages_down) * (pages_down - 1 - y)
|
764
|
+
if ratio_w >= ratio_h
|
765
|
+
@page_width, @page_height = width, height
|
766
|
+
else
|
767
|
+
@page_width, @page_height = width, width / ratio_w
|
768
|
+
end
|
769
|
+
end
|
770
|
+
|
771
|
+
gw.save_graphics_state
|
772
|
+
gw.concat_matrix(*@sub_page)
|
773
|
+
@last_font = @last_page_font = nil
|
774
|
+
end
|
775
|
+
|
776
|
+
def move_to(x, y)
|
777
|
+
@loc = translate(x, y)
|
778
|
+
nil
|
779
|
+
end
|
780
|
+
|
781
|
+
def pen_pos(x=nil, y=nil)
|
782
|
+
return translate(@loc.x, @loc.y) if x.nil?
|
783
|
+
prev_loc = translate(@loc.x, @loc.y)
|
784
|
+
move_to(x, y)
|
785
|
+
prev_loc
|
786
|
+
end
|
787
|
+
|
788
|
+
def move_by(dx, dy)
|
789
|
+
p = pen_pos
|
790
|
+
move_to(p.x + dx, p.y + dy)
|
791
|
+
end
|
792
|
+
|
793
|
+
def line_to(x, y)
|
794
|
+
unless @last_loc == @loc
|
795
|
+
gw.stroke if @in_path and @auto_path
|
796
|
+
@in_path = false
|
797
|
+
end
|
798
|
+
|
799
|
+
check_set(:line_color, :line_width, :line_dash_pattern)
|
800
|
+
|
801
|
+
gw.move_to(to_points(@units, @loc.x), to_points(@units, @loc.y)) unless @in_path
|
802
|
+
move_to(x, y)
|
803
|
+
gw.line_to(to_points(@units, @loc.x), to_points(@units, @loc.y))
|
804
|
+
@in_path = true
|
805
|
+
@last_loc = @loc.clone
|
806
|
+
nil
|
807
|
+
end
|
808
|
+
|
809
|
+
def line(x, y, angle, length)
|
810
|
+
lx, ly = rotate_xy_coordinate(1, 0, angle)
|
811
|
+
move_to(x, y)
|
812
|
+
line_to(x + lx * length, y - ly * length)
|
813
|
+
end
|
814
|
+
|
815
|
+
def rectangle(x, y, width, height, options={}, &block)
|
816
|
+
border = options[:border].nil? ? true : options[:border]
|
817
|
+
fill = options[:fill].nil? ? false : options[:fill]
|
818
|
+
clip = options[:clip].nil? ? false : options[:clip] && block_given?
|
819
|
+
gw.stroke if @in_path and @auto_path
|
820
|
+
|
821
|
+
line_colors.push(border)
|
822
|
+
fill_colors.push(fill)
|
823
|
+
check_set(:line_color, :line_width, :line_dash_pattern, :fill_color)
|
824
|
+
|
825
|
+
if options[:corners]
|
826
|
+
draw_rounded_rectangle(x, y, width, height, options)
|
827
|
+
elsif options[:path] or options[:reverse]
|
828
|
+
draw_rectangle_path(x, y, width, height, options)
|
829
|
+
else
|
830
|
+
gw.rectangle(
|
831
|
+
to_points(@units, x),
|
832
|
+
@page_height - to_points(@units, y + height),
|
833
|
+
to_points(@units, width),
|
834
|
+
to_points(@units, height))
|
835
|
+
end
|
836
|
+
|
837
|
+
gw.save_graphics_state if clip
|
838
|
+
auto_stroke_and_fill(:stroke => border, :fill => fill, :clip => clip)
|
839
|
+
yield if block_given?
|
840
|
+
gw.restore_graphics_state if clip
|
841
|
+
line_colors.pop
|
842
|
+
fill_colors.pop
|
843
|
+
move_to(x + width, y)
|
844
|
+
nil
|
845
|
+
end
|
846
|
+
|
847
|
+
def curve(x0, y0, x1, y1, x2, y2, x3, y3)
|
848
|
+
move_to(x0, y0)
|
849
|
+
unless @last_loc == @loc
|
850
|
+
if @in_path and @auto_path
|
851
|
+
gw.stroke
|
852
|
+
@in_path = false
|
853
|
+
end
|
854
|
+
end
|
855
|
+
check_set(:line_color, :line_width, :line_dash_pattern)
|
856
|
+
|
857
|
+
gw.move_to(to_points(@units, @loc.x), to_points(@units, @loc.y)) unless @in_path
|
858
|
+
gw.curve_to(
|
859
|
+
to_points(@units, x1),
|
860
|
+
@page_height - to_points(@units, y1),
|
861
|
+
to_points(@units, x2),
|
862
|
+
@page_height - to_points(@units, y2),
|
863
|
+
to_points(@units, x3),
|
864
|
+
@page_height - to_points(@units, y3))
|
865
|
+
move_to(x3, y3)
|
866
|
+
@last_loc = @loc.clone
|
867
|
+
@in_path = true
|
868
|
+
end
|
869
|
+
|
870
|
+
def curve_points(points)
|
871
|
+
raise Exception.new("Need at least 4 points for curve") if points.size < 4
|
872
|
+
move_to(points[0].x, points[0].y)
|
873
|
+
unless @last_loc == @loc
|
874
|
+
if @in_path and @auto_path
|
875
|
+
gw.stroke
|
876
|
+
@in_path = false
|
877
|
+
end
|
878
|
+
end
|
879
|
+
|
880
|
+
check_set(:line_color, :line_width, :line_dash_pattern)
|
881
|
+
|
882
|
+
gw.move_to(to_points(@units, @loc.x), to_points(@units, @loc.y)) unless (@loc == @last_loc) and @in_path
|
883
|
+
i = 1
|
884
|
+
while i + 2 < points.size
|
885
|
+
gw.curve_to(
|
886
|
+
to_points(@units, points[i].x),
|
887
|
+
@page_height - to_points(@units, points[i].y),
|
888
|
+
to_points(@units, points[i+1].x),
|
889
|
+
@page_height - to_points(@units, points[i+1].y),
|
890
|
+
to_points(@units, points[i+2].x),
|
891
|
+
@page_height - to_points(@units, points[i+2].y)
|
892
|
+
)
|
893
|
+
move_to(points[i+2].x, points[i+2].y)
|
894
|
+
@last_loc = @loc.clone
|
895
|
+
i += 3
|
896
|
+
end
|
897
|
+
@in_path = true
|
898
|
+
end
|
899
|
+
|
900
|
+
# def curve_to(points)
|
901
|
+
# end
|
902
|
+
|
903
|
+
def points_for_circle(x, y, r)
|
904
|
+
points = (1..4).inject([]) { |points, q| points + get_quadrant_bezier_points(q, x, y, r) }
|
905
|
+
[12,8,4].each { |i| points.delete_at(i) }
|
906
|
+
points
|
907
|
+
end
|
908
|
+
|
909
|
+
def circle(x, y, r, options={}, &block)
|
910
|
+
border = options[:border].nil? ? true : options[:border]
|
911
|
+
fill = options[:fill].nil? ? false : options[:fill]
|
912
|
+
clip = options[:clip].nil? ? false : options[:clip] && block_given?
|
913
|
+
|
914
|
+
line_colors.push(border)
|
915
|
+
fill_colors.push(fill)
|
916
|
+
check_set(:line_color, :line_width, :line_dash_pattern, :fill_color)
|
917
|
+
|
918
|
+
points = points_for_circle(x, y, r)
|
919
|
+
points.reverse! if options[:reverse]
|
920
|
+
curve_points(points)
|
921
|
+
|
922
|
+
gw.save_graphics_state if clip
|
923
|
+
auto_stroke_and_fill(:stroke => border, :fill => fill, :clip => clip)
|
924
|
+
yield if block_given?
|
925
|
+
gw.restore_graphics_state if clip
|
926
|
+
line_colors.pop
|
927
|
+
fill_colors.pop
|
928
|
+
nil
|
929
|
+
end
|
930
|
+
|
931
|
+
def points_for_ellipse(x, y, rx, ry)
|
932
|
+
points = (1..4).inject([]) { |points, q| points + get_quadrant_bezier_points(q, x, y, rx, ry) }
|
933
|
+
[12,8,4].each { |i| points.delete_at(i) }
|
934
|
+
points
|
935
|
+
end
|
936
|
+
|
937
|
+
def ellipse(x, y, rx, ry, options={}, &block)
|
938
|
+
rotation = options[:rotation] || 0
|
939
|
+
border = options[:border].nil? ? true : options[:border]
|
940
|
+
fill = options[:fill].nil? ? false : options[:fill]
|
941
|
+
clip = options[:clip].nil? ? false : options[:clip] && block_given?
|
942
|
+
|
943
|
+
line_colors.push(border)
|
944
|
+
fill_colors.push(fill)
|
945
|
+
check_set(:line_color, :line_width, :line_dash_pattern, :fill_color)
|
946
|
+
|
947
|
+
points = points_for_ellipse(x, y, rx, ry)
|
948
|
+
points = rotate_points(make_loc(x, y), points, -rotation)
|
949
|
+
points.reverse! if options[:reverse]
|
950
|
+
curve_points(points)
|
951
|
+
|
952
|
+
gw.save_graphics_state if clip
|
953
|
+
auto_stroke_and_fill(:stroke => border, :fill => fill, :clip => clip)
|
954
|
+
yield if block_given?
|
955
|
+
gw.restore_graphics_state if clip
|
956
|
+
line_colors.pop
|
957
|
+
fill_colors.pop
|
958
|
+
nil
|
959
|
+
end
|
960
|
+
|
961
|
+
def points_for_arc(x, y, r, start_angle, end_angle)
|
962
|
+
return nil if start_angle == end_angle
|
963
|
+
|
964
|
+
num_arcs = 1
|
965
|
+
ccwcw = 1.0
|
966
|
+
arc_span = end_angle - start_angle
|
967
|
+
if end_angle < start_angle
|
968
|
+
ccwcw = -1.0
|
969
|
+
end
|
970
|
+
while arc_span.abs.quo(num_arcs) > 90.0
|
971
|
+
num_arcs += 1
|
972
|
+
end
|
973
|
+
angle_bump = arc_span.quo(num_arcs)
|
974
|
+
half_bump = 0.5 * angle_bump
|
975
|
+
cur_angle = start_angle + half_bump
|
976
|
+
points = []
|
977
|
+
num_arcs.times do |i|
|
978
|
+
points << points_for_arc_small(x, y, r, cur_angle, half_bump, ccwcw)
|
979
|
+
points.last.shift if i > 0
|
980
|
+
cur_angle = cur_angle + angle_bump
|
981
|
+
end
|
982
|
+
points.flatten
|
983
|
+
end
|
984
|
+
|
985
|
+
def arc(x, y, r, start_angle, end_angle, move_to0=false)
|
986
|
+
return if start_angle == end_angle
|
987
|
+
|
988
|
+
move_to0 = true unless @in_path
|
989
|
+
num_arcs = 1
|
990
|
+
ccwcw = 1.0
|
991
|
+
arc_span = end_angle - start_angle
|
992
|
+
if end_angle < start_angle
|
993
|
+
ccwcw = -1.0
|
994
|
+
end
|
995
|
+
while arc_span.abs.quo(num_arcs) > 90.0
|
996
|
+
num_arcs += 1
|
997
|
+
end
|
998
|
+
angle_bump = arc_span.quo(num_arcs)
|
999
|
+
half_bump = 0.5 * angle_bump
|
1000
|
+
cur_angle = start_angle + half_bump
|
1001
|
+
num_arcs.times do
|
1002
|
+
arc_small(x, y, r, cur_angle, half_bump, ccwcw, move_to0)
|
1003
|
+
move_to0 = false
|
1004
|
+
cur_angle = cur_angle + angle_bump
|
1005
|
+
end
|
1006
|
+
end
|
1007
|
+
|
1008
|
+
def pie(x, y, r, start_angle, end_angle, options={})
|
1009
|
+
border = options[:border].nil? ? true : options[:border]
|
1010
|
+
fill = options[:fill].nil? ? false : options[:fill]
|
1011
|
+
clip = options[:clip].nil? ? false : options[:clip] && block_given?
|
1012
|
+
unless @last_loc == @loc
|
1013
|
+
gw.stroke if @in_path and @auto_path
|
1014
|
+
@in_path = false
|
1015
|
+
end
|
1016
|
+
|
1017
|
+
line_colors.push(border)
|
1018
|
+
fill_colors.push(fill)
|
1019
|
+
check_set(:line_color, :line_width, :line_dash_pattern, :fill_color)
|
1020
|
+
|
1021
|
+
move_to(x, y)
|
1022
|
+
gw.move_to(to_points(@units, @loc.x), to_points(@units, @loc.y))
|
1023
|
+
@last_loc = @loc.clone
|
1024
|
+
@in_path = true
|
1025
|
+
start_angle, end_angle = end_angle, start_angle if options[:reverse]
|
1026
|
+
arc(x, y, r, start_angle, end_angle)
|
1027
|
+
line_to(x, y)
|
1028
|
+
|
1029
|
+
gw.save_graphics_state if clip
|
1030
|
+
auto_stroke_and_fill(:stroke => border, :fill => fill, :clip => clip)
|
1031
|
+
yield if block_given?
|
1032
|
+
gw.restore_graphics_state if clip
|
1033
|
+
line_colors.pop
|
1034
|
+
fill_colors.pop
|
1035
|
+
nil
|
1036
|
+
end
|
1037
|
+
|
1038
|
+
def arch(x, y, r1, r2, start_angle, end_angle, options={}, &block)
|
1039
|
+
return if start_angle == end_angle
|
1040
|
+
start_angle, end_angle = end_angle, start_angle if options[:reverse]
|
1041
|
+
border = options[:border].nil? ? true : options[:border]
|
1042
|
+
fill = options[:fill].nil? ? false : options[:fill]
|
1043
|
+
clip = options[:clip].nil? ? false : options[:clip] && block_given?
|
1044
|
+
unless @last_loc == @loc
|
1045
|
+
gw.stroke if @in_path and @auto_path
|
1046
|
+
@in_path = false
|
1047
|
+
end
|
1048
|
+
|
1049
|
+
line_colors.push(border)
|
1050
|
+
fill_colors.push(fill)
|
1051
|
+
check_set(:line_color, :line_width, :line_dash_pattern, :fill_color)
|
1052
|
+
arc1 = points_for_arc(x, y, r1, start_angle, end_angle)
|
1053
|
+
arc2 = points_for_arc(x, y, r2, end_angle, start_angle)
|
1054
|
+
move_to(arc1.first.x, arc1.first.y)
|
1055
|
+
gw.move_to(to_points(@units, @loc.x), to_points(@units, @loc.y))
|
1056
|
+
curve_points(arc1)
|
1057
|
+
line_to(arc2.first.x, arc2.first.y)
|
1058
|
+
curve_points(arc2)
|
1059
|
+
line_to(arc1.first.x, arc1.first.y)
|
1060
|
+
|
1061
|
+
gw.save_graphics_state if clip
|
1062
|
+
auto_stroke_and_fill(:stroke => border, :fill => fill, :clip => clip)
|
1063
|
+
yield if block_given?
|
1064
|
+
gw.restore_graphics_state if clip
|
1065
|
+
line_colors.pop
|
1066
|
+
fill_colors.pop
|
1067
|
+
nil
|
1068
|
+
end
|
1069
|
+
|
1070
|
+
def points_for_polygon(x, y, r, sides, options={})
|
1071
|
+
step = 360.0 / sides
|
1072
|
+
angle = step / 2 + 90
|
1073
|
+
points = (0..sides).collect do
|
1074
|
+
px, py = rotate_xy_coordinate(1, 0, angle)
|
1075
|
+
angle += step
|
1076
|
+
make_loc(x + px * r, y + py * r)
|
1077
|
+
end
|
1078
|
+
rotation = options[:rotation] || 0
|
1079
|
+
points = rotate_points(make_loc(x, y), points, -rotation) unless rotation.zero?
|
1080
|
+
points.reverse! if options[:reverse]
|
1081
|
+
points
|
1082
|
+
end
|
1083
|
+
|
1084
|
+
def polygon(x, y, r, sides, options={}, &block)
|
1085
|
+
border = options[:border].nil? ? true : options[:border]
|
1086
|
+
fill = options[:fill].nil? ? false : options[:fill]
|
1087
|
+
clip = options[:clip].nil? ? false : options[:clip] && block_given?
|
1088
|
+
unless @last_loc == @loc
|
1089
|
+
gw.stroke if @in_path and @auto_path
|
1090
|
+
@in_path = false
|
1091
|
+
end
|
1092
|
+
|
1093
|
+
points = points_for_polygon(x, y, r, sides, options)
|
1094
|
+
line_colors.push(border)
|
1095
|
+
fill_colors.push(fill)
|
1096
|
+
check_set(:line_color, :line_width, :line_dash_pattern, :fill_color)
|
1097
|
+
|
1098
|
+
points.each_with_index do |point, i|
|
1099
|
+
if i == 0
|
1100
|
+
move_to(point.x, point.y)
|
1101
|
+
else
|
1102
|
+
line_to(point.x, point.y)
|
1103
|
+
end
|
1104
|
+
end
|
1105
|
+
gw.save_graphics_state if clip
|
1106
|
+
auto_stroke_and_fill(:stroke => border, :fill => fill, :clip => clip)
|
1107
|
+
yield if block_given?
|
1108
|
+
gw.restore_graphics_state if clip
|
1109
|
+
line_colors.pop
|
1110
|
+
fill_colors.pop
|
1111
|
+
nil
|
1112
|
+
end
|
1113
|
+
|
1114
|
+
def star(x, y, r1, r2, points, options={}, &block)
|
1115
|
+
return if points < 5
|
1116
|
+
border = options[:border].nil? ? true : options[:border]
|
1117
|
+
fill = options[:fill].nil? ? false : options[:fill]
|
1118
|
+
clip = options[:clip].nil? ? false : options[:clip] && block_given?
|
1119
|
+
unless @last_loc == @loc
|
1120
|
+
gw.stroke if @in_path and @auto_path
|
1121
|
+
@in_path = false
|
1122
|
+
end
|
1123
|
+
|
1124
|
+
rotation = options[:rotation] || 0
|
1125
|
+
r2 ||= (points - 2).quo(points * 1.5)
|
1126
|
+
vertices1 = points_for_polygon(x, y, r1, points, options)
|
1127
|
+
vertices2 = points_for_polygon(x, y, r2, points, options.merge(:rotation => rotation + (360.0 / points / 2)))
|
1128
|
+
line_colors.push(border)
|
1129
|
+
fill_colors.push(fill)
|
1130
|
+
check_set(:line_color, :line_width, :line_dash_pattern, :fill_color)
|
1131
|
+
|
1132
|
+
move_to(vertices2[0].x, vertices2[0].y)
|
1133
|
+
points.times do |i|
|
1134
|
+
line_to(vertices1[i].x, vertices1[i].y)
|
1135
|
+
line_to(vertices2[i+1].x, vertices2[i+1].y)
|
1136
|
+
end
|
1137
|
+
gw.save_graphics_state if clip
|
1138
|
+
auto_stroke_and_fill(:stroke => border, :fill => fill, :clip => clip)
|
1139
|
+
yield if block_given?
|
1140
|
+
gw.restore_graphics_state if clip
|
1141
|
+
line_colors.pop
|
1142
|
+
fill_colors.pop
|
1143
|
+
nil
|
1144
|
+
end
|
1145
|
+
|
1146
|
+
def path(options={}, &block)
|
1147
|
+
check_set(:line_color, :line_width, :line_dash_pattern, :fill_color)
|
1148
|
+
stroke = options[:stroke].nil? ? false : options[:stroke]
|
1149
|
+
fill = options[:fill].nil? ? false : options[:fill]
|
1150
|
+
line_colors.push(stroke)
|
1151
|
+
fill_colors.push(fill)
|
1152
|
+
@auto_path = false
|
1153
|
+
if block_given?
|
1154
|
+
yield
|
1155
|
+
if options[:fill] and options[:stroke]
|
1156
|
+
gw.fill_and_stroke
|
1157
|
+
elsif options[:stroke]
|
1158
|
+
gw.stroke
|
1159
|
+
elsif options[:fill]
|
1160
|
+
gw.fill
|
1161
|
+
else
|
1162
|
+
gw.new_path
|
1163
|
+
end
|
1164
|
+
line_colors.pop
|
1165
|
+
fill_colors.pop
|
1166
|
+
@in_path = false
|
1167
|
+
@auto_path = true
|
1168
|
+
end
|
1169
|
+
end
|
1170
|
+
|
1171
|
+
def fill
|
1172
|
+
raise Exception.new("Not in graph") unless @in_graph
|
1173
|
+
raise Exception.new("Not in path") unless @in_path
|
1174
|
+
|
1175
|
+
check_set(:fill_color)
|
1176
|
+
gw.fill
|
1177
|
+
line_colors.pop
|
1178
|
+
fill_colors.pop
|
1179
|
+
@in_path = false
|
1180
|
+
@auto_path = true
|
1181
|
+
end
|
1182
|
+
|
1183
|
+
def stroke
|
1184
|
+
raise Exception.new("Not in graph") unless @in_graph
|
1185
|
+
raise Exception.new("Not in path") unless @in_path
|
1186
|
+
|
1187
|
+
check_set(:line_color)
|
1188
|
+
gw.stroke
|
1189
|
+
line_colors.pop
|
1190
|
+
fill_colors.pop
|
1191
|
+
@in_path = false
|
1192
|
+
@auto_path = true
|
1193
|
+
end
|
1194
|
+
|
1195
|
+
def fill_and_stroke
|
1196
|
+
raise Exception.new("Not in graph") unless @in_graph
|
1197
|
+
raise Exception.new("Not in path") unless @in_path
|
1198
|
+
|
1199
|
+
check_set(:fill_color,:line_color)
|
1200
|
+
gw.fill_and_stroke
|
1201
|
+
line_colors.pop
|
1202
|
+
fill_colors.pop
|
1203
|
+
@in_path = false
|
1204
|
+
@auto_path = true
|
1205
|
+
end
|
1206
|
+
|
1207
|
+
def clip(options={}, &block)
|
1208
|
+
gw.save_graphics_state
|
1209
|
+
if @in_path
|
1210
|
+
gw.clip
|
1211
|
+
if options[:fill] and options[:stroke]
|
1212
|
+
gw.fill_and_stroke
|
1213
|
+
elsif options[:stroke]
|
1214
|
+
gw.stroke
|
1215
|
+
elsif options[:fill]
|
1216
|
+
gw.fill
|
1217
|
+
else
|
1218
|
+
gw.new_path
|
1219
|
+
end
|
1220
|
+
end
|
1221
|
+
save_text_rendering_mode = @text_rendering_mode
|
1222
|
+
text_clipping_mode(options)
|
1223
|
+
yield if block_given?
|
1224
|
+
# gw.clip
|
1225
|
+
gw.restore_graphics_state
|
1226
|
+
@text_rendering_mode = save_text_rendering_mode
|
1227
|
+
@in_path = false
|
1228
|
+
@auto_path = true
|
1229
|
+
end
|
1230
|
+
|
1231
|
+
def line_cap_style(style=nil)
|
1232
|
+
return @line_cap_style || :butt_cap if style.nil?
|
1233
|
+
prev_line_cap_style, @line_cap_style = @line_cap_style, style.to_sym if LINE_CAP_STYLES.include?(style.to_sym)
|
1234
|
+
prev_line_cap_style
|
1235
|
+
end
|
1236
|
+
|
1237
|
+
def line_dash_pattern(pattern=nil)
|
1238
|
+
return @line_dash_pattern if pattern.nil?
|
1239
|
+
prev_line_dash_pattern, @line_dash_pattern = @line_dash_pattern, pattern
|
1240
|
+
prev_line_dash_pattern
|
1241
|
+
end
|
1242
|
+
|
1243
|
+
def line_width(value=nil, units=nil)
|
1244
|
+
return from_points(@units, @line_width || 0) if value.nil?
|
1245
|
+
return from_points(value, @line_width || 0) if value.is_a?(Symbol)
|
1246
|
+
prev_line_width = @line_width || 0
|
1247
|
+
if !units.nil?
|
1248
|
+
u, value = units.to_sym, value.to_f
|
1249
|
+
elsif value.respond_to?(:to_str) and value =~ /\D+/
|
1250
|
+
u, value = $&.to_sym, value.to_f
|
1251
|
+
else
|
1252
|
+
u = @units
|
1253
|
+
end
|
1254
|
+
@line_width = to_points(u, value)
|
1255
|
+
from_points(@units, prev_line_width)
|
1256
|
+
end
|
1257
|
+
|
1258
|
+
def line_height(height=nil)
|
1259
|
+
return @line_height if height.nil?
|
1260
|
+
prev_line_height, @line_height = @line_height, height
|
1261
|
+
prev_line_height
|
1262
|
+
end
|
1263
|
+
|
1264
|
+
def named_colors
|
1265
|
+
@doc.named_colors
|
1266
|
+
end
|
1267
|
+
|
1268
|
+
def line_color(color=nil)
|
1269
|
+
return @line_color if color.nil?
|
1270
|
+
if color.is_a?(Array)
|
1271
|
+
r, g, b = color
|
1272
|
+
prev_line_color, @line_color = @line_color, color_from_rgb(r, g, b)
|
1273
|
+
else
|
1274
|
+
prev_line_color, @line_color = @line_color, color
|
1275
|
+
end
|
1276
|
+
prev_line_color
|
1277
|
+
end
|
1278
|
+
|
1279
|
+
def fill_color(color=nil)
|
1280
|
+
return @fill_color if color.nil?
|
1281
|
+
if color.is_a?(Array)
|
1282
|
+
r, g, b = color
|
1283
|
+
prev_fill_color, @fill_color = @fill_color, color_from_rgb(r, g, b)
|
1284
|
+
else
|
1285
|
+
prev_fill_color, @fill_color = @fill_color, color
|
1286
|
+
end
|
1287
|
+
prev_fill_color
|
1288
|
+
end
|
1289
|
+
|
1290
|
+
def print(text, options={}, &block)
|
1291
|
+
text = text.to_s
|
1292
|
+
return if text.empty?
|
1293
|
+
text = @ic.iconv(text) unless @ic.nil?
|
1294
|
+
align = options[:align]
|
1295
|
+
angle = options[:angle] || 0.0
|
1296
|
+
@scale = options[:scale] || 1.0
|
1297
|
+
prev_underline = underline(options[:underline]) unless options[:underline].nil?
|
1298
|
+
prev_v_text_align = v_text_align(options[:v_text_align]) unless options[:v_text_align].nil?
|
1299
|
+
clip = options[:clip] && block_given?
|
1300
|
+
if clip
|
1301
|
+
gw.save_graphics_state
|
1302
|
+
text_clipping_mode(options)
|
1303
|
+
end
|
1304
|
+
start_text unless @in_text
|
1305
|
+
check_set(:font, :font_color, :line_color, :v_text_align, :spacing, :scale, :text_rendering_mode)
|
1306
|
+
ds = width(text, true)
|
1307
|
+
if align
|
1308
|
+
prev_loc = @loc.clone
|
1309
|
+
@loc = case align
|
1310
|
+
when :left then @loc
|
1311
|
+
when :center then add_vector(@loc, angle + 180, ds.quo(2))
|
1312
|
+
when :right then add_vector(@loc, angle + 180, ds)
|
1313
|
+
end
|
1314
|
+
end
|
1315
|
+
if (@text_angle != angle) or (angle != 0.0)
|
1316
|
+
set_text_angle(angle, @loc.x, @loc.y)
|
1317
|
+
elsif @loc != @last_loc
|
1318
|
+
tw.move_by(to_points(@units, @loc.x - @last_loc.x), to_points(@units, @loc.y - @last_loc.y))
|
1319
|
+
end
|
1320
|
+
tw.show(text)
|
1321
|
+
# if @ic.nil?
|
1322
|
+
# tw.show(text)
|
1323
|
+
# else
|
1324
|
+
# text = @ic.iconv(text)
|
1325
|
+
# tw.show_wide(text)
|
1326
|
+
# end
|
1327
|
+
@last_loc = @loc.clone
|
1328
|
+
new_loc = (angle == 0.0) ? Location.new(@loc.x + ds, @loc.y) : add_vector(@loc, angle, ds)
|
1329
|
+
draw_underline(translate_p(@last_loc), translate_p(new_loc), @font.underline_position, @font.underline_thickness, angle) if @underline
|
1330
|
+
underline(prev_underline) unless options[:underline].nil?
|
1331
|
+
v_text_align(prev_v_text_align) unless options[:v_text_align].nil?
|
1332
|
+
@loc = align ? prev_loc : new_loc
|
1333
|
+
if clip
|
1334
|
+
yield
|
1335
|
+
gw.restore_graphics_state
|
1336
|
+
end
|
1337
|
+
nil
|
1338
|
+
end
|
1339
|
+
|
1340
|
+
def print_xy(x, y, text, options={}, &block)
|
1341
|
+
move_to(x, y)
|
1342
|
+
print(text, options, &block)
|
1343
|
+
end
|
1344
|
+
|
1345
|
+
def puts(text='', options={}, &block)
|
1346
|
+
# if it's not a real string, assume it's an enumeration of strings
|
1347
|
+
unless text.respond_to?(:to_str)
|
1348
|
+
text.each { |t| puts(t, options) }
|
1349
|
+
else
|
1350
|
+
prev_loc = @loc.clone
|
1351
|
+
print(text, options, &block)
|
1352
|
+
@loc = options[:indent] ? Location.new(prev_loc.x, prev_loc.y - height) : Location.new(@indent, prev_loc.y - height)
|
1353
|
+
end
|
1354
|
+
nil
|
1355
|
+
end
|
1356
|
+
|
1357
|
+
def puts_xy(x, y, text, options={}, &block)
|
1358
|
+
move_to(x, y)
|
1359
|
+
prev_indent = indent(x, true)
|
1360
|
+
puts(text, options, &block)
|
1361
|
+
indent(prev_indent, true)
|
1362
|
+
nil
|
1363
|
+
end
|
1364
|
+
|
1365
|
+
def new_line(count=1)
|
1366
|
+
@loc = Location.new(@indent, @loc.y - height * count)
|
1367
|
+
nil
|
1368
|
+
end
|
1369
|
+
|
1370
|
+
def width(text, encoded=false)
|
1371
|
+
set_default_font if @font.nil?
|
1372
|
+
result = 0.0
|
1373
|
+
fsize = @font.size * 0.001
|
1374
|
+
text = @ic.iconv(text) unless @ic.nil? or encoded
|
1375
|
+
text.each_byte do |b|
|
1376
|
+
result += fsize * @font.widths[b] + @char_spacing
|
1377
|
+
result += @word_spacing if b == 32 # space
|
1378
|
+
end
|
1379
|
+
from_points(@units, result - @char_spacing)
|
1380
|
+
end
|
1381
|
+
|
1382
|
+
def wrap(text, width)
|
1383
|
+
re = /\n|\t|[ ]|[\S]+-+|[\S]+/
|
1384
|
+
words = text.scan(re)
|
1385
|
+
word_tuples = words.map { |word| [width(word), word] }
|
1386
|
+
line_length = 0
|
1387
|
+
lines = word_tuples.inject(['']) do |lines, tuple|
|
1388
|
+
if tuple[1] == "\n"
|
1389
|
+
lines << ''
|
1390
|
+
line_length = 0
|
1391
|
+
elsif line_length == 0
|
1392
|
+
unless tuple[1] == ' '
|
1393
|
+
lines.last << tuple[1]
|
1394
|
+
line_length += tuple[0]
|
1395
|
+
end
|
1396
|
+
elsif line_length + tuple[0] > width
|
1397
|
+
lines << ''
|
1398
|
+
line_length = 0
|
1399
|
+
redo
|
1400
|
+
else
|
1401
|
+
lines.last << tuple[1]
|
1402
|
+
line_length += tuple[0]
|
1403
|
+
end
|
1404
|
+
lines
|
1405
|
+
end
|
1406
|
+
end
|
1407
|
+
|
1408
|
+
def text_ascent(units=nil)
|
1409
|
+
units ||= @units
|
1410
|
+
set_default_font if @font.nil?
|
1411
|
+
0.001 * @font.ascent * @font.size.quo(UNIT_CONVERSION[units])
|
1412
|
+
end
|
1413
|
+
|
1414
|
+
def text_height(units=nil)
|
1415
|
+
units ||= @units
|
1416
|
+
set_default_font if @font.nil?
|
1417
|
+
0.001 * @font.height * @font.size.quo(UNIT_CONVERSION[units])
|
1418
|
+
end
|
1419
|
+
|
1420
|
+
def height(text='', units=nil)
|
1421
|
+
units ||= @units
|
1422
|
+
set_default_font if @font.nil?
|
1423
|
+
if text.respond_to?(:to_str)
|
1424
|
+
text_height(units) * @line_height
|
1425
|
+
else
|
1426
|
+
text.inject(0) { |total, line| total + height(line, units) }
|
1427
|
+
end
|
1428
|
+
end
|
1429
|
+
|
1430
|
+
def paragraph(text, options={})
|
1431
|
+
width = options[:width] || canvas_width - pen_pos.x
|
1432
|
+
height = options[:height] || canvas_height - pen_pos.y
|
1433
|
+
if bul = bullet(options[:bullet])
|
1434
|
+
save_loc = pen_pos
|
1435
|
+
bul.proc.call(self)
|
1436
|
+
move_to(save_loc.x + from_points(units, bul.width), save_loc.y)
|
1437
|
+
width -= from_points(units, bul.width)
|
1438
|
+
end
|
1439
|
+
prev_underline, ul = @underline, options[:underline].nil? ? @underline : options[:underline]
|
1440
|
+
unless text.is_a?(PdfText::RichText)
|
1441
|
+
text = PdfText::RichText.new(text, font, :color => @font_color,
|
1442
|
+
:char_spacing => @char_spacing, :word_spacing => @word_spacing,
|
1443
|
+
:underline => ul)
|
1444
|
+
end
|
1445
|
+
dy = 0
|
1446
|
+
while dy + from_points(@units, text.height) < height and line = text.next(to_points(@units, width))
|
1447
|
+
save_loc = pen_pos
|
1448
|
+
line_dy = line.height.quo(UNIT_CONVERSION[units]) * @line_height
|
1449
|
+
case options[:align]
|
1450
|
+
when :center then move_to(save_loc.x + (width - from_points(@units, line.width)) / 2.0, save_loc.y)
|
1451
|
+
when :right then move_to(save_loc.x + width - from_points(@units, line.width), save_loc.y)
|
1452
|
+
when :justify then
|
1453
|
+
width_pt = to_points(@units, width)
|
1454
|
+
delta_pt = width_pt - line.width
|
1455
|
+
words = (line.tokens / 2) + 1
|
1456
|
+
if delta_pt.abs.quo(width_pt) < 0.4
|
1457
|
+
if words == 1
|
1458
|
+
@word_spacing = 0
|
1459
|
+
@char_spacing = delta_pt.quo(line.chars - 1)
|
1460
|
+
elsif delta_pt.abs / words > 3
|
1461
|
+
@word_spacing = 3
|
1462
|
+
delta_pt -= (words - 1) * @word_spacing
|
1463
|
+
@char_spacing = delta_pt.quo(line.chars - 1)
|
1464
|
+
else
|
1465
|
+
@word_spacing = delta_pt.quo(words - 1)
|
1466
|
+
@char_spacing = 0
|
1467
|
+
end
|
1468
|
+
end
|
1469
|
+
end
|
1470
|
+
while piece = line.shift
|
1471
|
+
font(piece.font)
|
1472
|
+
font_color piece.color
|
1473
|
+
underline piece.underline
|
1474
|
+
print(piece.text)
|
1475
|
+
end
|
1476
|
+
@word_spacing = @char_spacing = 0.0 if options[:align] == :justify
|
1477
|
+
dy += line_dy
|
1478
|
+
move_to(save_loc.x, save_loc.y + line_dy)
|
1479
|
+
end
|
1480
|
+
underline(prev_underline)
|
1481
|
+
move_by(-from_points(units, bul.width), 0) unless bul.nil?
|
1482
|
+
return text.empty? ? nil : text
|
1483
|
+
end
|
1484
|
+
|
1485
|
+
def paragraph_xy(x, y, text, options={})
|
1486
|
+
move_to(x, y)
|
1487
|
+
paragraph(text, options)
|
1488
|
+
end
|
1489
|
+
|
1490
|
+
def v_text_align(vta=nil)
|
1491
|
+
return @v_text_align if vta.nil?
|
1492
|
+
prev_vta, @v_text_align = @v_text_align, vta
|
1493
|
+
prev_vta
|
1494
|
+
end
|
1495
|
+
|
1496
|
+
def underline(underline=nil)
|
1497
|
+
return @underline if underline.nil?
|
1498
|
+
prev_underline, @underline = @underline, underline
|
1499
|
+
prev_underline
|
1500
|
+
end
|
1501
|
+
|
1502
|
+
def type1_font_names
|
1503
|
+
@doc.type1_font_names
|
1504
|
+
end
|
1505
|
+
|
1506
|
+
def truetype_font_names
|
1507
|
+
@doc.truetype_font_names
|
1508
|
+
end
|
1509
|
+
|
1510
|
+
def select_font(name, size, options={})
|
1511
|
+
unless @ic.nil?
|
1512
|
+
@ic.close
|
1513
|
+
@ic = nil
|
1514
|
+
end
|
1515
|
+
weight = options[:weight] || ''
|
1516
|
+
style = weight + (options[:style] || '')
|
1517
|
+
font = Font.new(name, size, style, options[:color], pdf_encoding(options[:encoding] || 'WinAnsiEncoding', name))
|
1518
|
+
font.sub_type = options[:sub_type] || 'Type1'
|
1519
|
+
punc = (font.sub_type == 'TrueType') ? ',' : '-'
|
1520
|
+
full_name = name.gsub(' ','')
|
1521
|
+
full_name << punc << font.style unless font.style.empty?
|
1522
|
+
if @options[:built_in_fonts]
|
1523
|
+
if font.sub_type == 'Type1'
|
1524
|
+
metrics = PdfK::font_metrics(full_name)
|
1525
|
+
elsif font.sub_type == 'TrueType'
|
1526
|
+
metrics = PdfTT::font_metrics(full_name)
|
1527
|
+
end
|
1528
|
+
else
|
1529
|
+
if font.sub_type == 'Type1'
|
1530
|
+
weight = 'Bold' if weight.empty? and /Bold/i =~ style
|
1531
|
+
afm = AFM::find_font(name, weight, style)
|
1532
|
+
full_name = afm.font_name unless afm.nil?
|
1533
|
+
metrics = AFM::font_metrics(full_name, :encoding => font.encoding)
|
1534
|
+
elsif font.sub_type == 'TrueType'
|
1535
|
+
raise Exception.new("Non-built-in TrueType fonts not supported yet.")
|
1536
|
+
elsif font.sub_type == 'Type0'
|
1537
|
+
# metrics = AFM::font_metrics(full_name, :encoding => :unicode)
|
1538
|
+
# require 'iconv'
|
1539
|
+
# @ic = Iconv.new('UCS-2BE', iconv_encoding(font.encoding))
|
1540
|
+
else
|
1541
|
+
raise Exception.new("Unsupported subtype #{font.sub_type}.")
|
1542
|
+
end
|
1543
|
+
end
|
1544
|
+
unless text_encoding.nil? or (font.encoding == 'StandardEncoding') or (font.encoding == text_encoding)
|
1545
|
+
@ic = Iconv.new(iconv_encoding(font.encoding)+'//IGNORE', iconv_encoding(text_encoding))
|
1546
|
+
end
|
1547
|
+
font.widths, font.ascent, font.descent = metrics.widths, metrics.ascent, metrics.descent
|
1548
|
+
font.height = font.ascent + font.descent.abs
|
1549
|
+
font.underline_position = metrics.underline_position * -0.001 * font.size
|
1550
|
+
font.underline_thickness = metrics.underline_thickness * 0.001 * font.size
|
1551
|
+
font_key = "#{full_name}/#{font.encoding}-#{font.sub_type}"
|
1552
|
+
page_font = @doc.fonts[font_key]
|
1553
|
+
unless page_font
|
1554
|
+
widths = nil
|
1555
|
+
if metrics.needs_descriptor
|
1556
|
+
descriptor = PdfObjects::PdfFontDescriptor.new(@doc.next_seq, 0, full_name, metrics.flags, metrics.b_box,
|
1557
|
+
metrics.missing_width, metrics.stem_v, metrics.stem_h, metrics.italic_angle,
|
1558
|
+
metrics.cap_height, metrics.x_height, metrics.ascent, metrics.descent, metrics.leading,
|
1559
|
+
metrics.max_width, metrics.avg_width)
|
1560
|
+
@doc.file.body << descriptor
|
1561
|
+
widths = PdfObjects::IndirectObject.new(@doc.next_seq, 0, PdfObjects::PdfInteger.ary(metrics.widths))
|
1562
|
+
@doc.file.body << widths
|
1563
|
+
else
|
1564
|
+
descriptor = nil
|
1565
|
+
widths = nil
|
1566
|
+
end
|
1567
|
+
page_font = "F#{@doc.fonts.size}"
|
1568
|
+
f = PdfObjects::PdfFont.new(@doc.next_seq, 0, font.sub_type, full_name, 0, 255, widths, descriptor)
|
1569
|
+
@doc.file.body << f
|
1570
|
+
if PdfObjects::PdfFont.standard_encoding?(font.encoding)
|
1571
|
+
f.encoding = font.encoding
|
1572
|
+
else
|
1573
|
+
encoding = @doc.encodings[font.encoding]
|
1574
|
+
if encoding.nil?
|
1575
|
+
encoding = PdfObjects::PdfFontEncoding.new(@doc.next_seq, 0, 'WinAnsiEncoding', metrics.differences)
|
1576
|
+
@doc.encodings[font.encoding] = encoding
|
1577
|
+
@doc.file.body << encoding
|
1578
|
+
end
|
1579
|
+
f.encoding = encoding.reference_object
|
1580
|
+
# raise Exception.new("Unsupported encoding #{font.encoding}")
|
1581
|
+
end
|
1582
|
+
@doc.resources.fonts[page_font] = f.reference_object
|
1583
|
+
@doc.fonts[font_key] = page_font
|
1584
|
+
end
|
1585
|
+
font_color(options[:color]) if options[:color]
|
1586
|
+
text_rendering_mode(options)
|
1587
|
+
[font, page_font]
|
1588
|
+
end
|
1589
|
+
|
1590
|
+
def font(name=nil, size=nil, options={})
|
1591
|
+
return @font || set_default_font if name.nil?
|
1592
|
+
prev_font = @font
|
1593
|
+
if name.is_a?(Font)
|
1594
|
+
@font = name
|
1595
|
+
name, size = @font.name, @font.size
|
1596
|
+
options.update(:style => @font.style, :color => @font.color, :encoding => @font.encoding, :sub_type => @font.sub_type)
|
1597
|
+
end
|
1598
|
+
size ||= @font.nil? ? @default_font[:size] : @font.size
|
1599
|
+
@font, @page_font = select_font(name, size, options)
|
1600
|
+
prev_font
|
1601
|
+
end
|
1602
|
+
|
1603
|
+
def font_style(style=nil)
|
1604
|
+
set_default_font if @font.nil?
|
1605
|
+
return @font.style if style.nil?
|
1606
|
+
prev_style = @font.style
|
1607
|
+
font(@font.name, @font.size, :style => style, :color => @font.color, :encoding => @font.encoding, :sub_type => @font.sub_type)
|
1608
|
+
prev_style
|
1609
|
+
end
|
1610
|
+
|
1611
|
+
def font_size(size=nil)
|
1612
|
+
set_default_font if @font.nil?
|
1613
|
+
return @font.size if size.nil?
|
1614
|
+
prev_size = @font.size
|
1615
|
+
font(@font.name, size, :style => @font.style, :color => @font.color, :encoding => @font.encoding, :sub_type => @font.sub_type)
|
1616
|
+
prev_size
|
1617
|
+
end
|
1618
|
+
|
1619
|
+
def font_color(color=nil)
|
1620
|
+
return @font_color if color.nil?
|
1621
|
+
if color.is_a?(Array)
|
1622
|
+
r, g, b = color
|
1623
|
+
prev_font_color, @font_color = @font_color, color_from_rgb(r, g, b)
|
1624
|
+
else
|
1625
|
+
prev_font_color, @font_color = @font_color, color
|
1626
|
+
end
|
1627
|
+
@font.color = @font_color unless @font.nil?
|
1628
|
+
prev_font_color
|
1629
|
+
end
|
1630
|
+
|
1631
|
+
def font_encoding(encoding=nil)
|
1632
|
+
set_default_font if @font.nil?
|
1633
|
+
return @font.encoding if encoding.nil?
|
1634
|
+
prev_encoding = @font.encoding
|
1635
|
+
font(@font.name, @font.size, :style => @font.style, :color => @font.color, :encoding => encoding, :sub_type => @font.sub_type)
|
1636
|
+
prev_encoding
|
1637
|
+
end
|
1638
|
+
|
1639
|
+
def load_image(image_file_name, stream=nil)
|
1640
|
+
image, name = @doc.images[image_file_name]
|
1641
|
+
return [image, name] unless image.nil?
|
1642
|
+
stream ||= open(image_file_name, ImageReadMode) { |io| io.read }
|
1643
|
+
image = PdfObjects::PdfImage.new(@doc.next_seq, 0, stream)
|
1644
|
+
image.width, image.height, components, image.bits_per_component = jpeg_dimensions(stream)
|
1645
|
+
image.color_space = { 1 => 'DeviceGray', 3 => 'DeviceRGB', 4 => 'DeviceCMYK' }[components]
|
1646
|
+
image.filter = 'DCTDecode'
|
1647
|
+
name = "Im#{@doc.images.size}"
|
1648
|
+
@doc.file.body << image
|
1649
|
+
@doc.resources.x_objects[name] = image.reference_object
|
1650
|
+
@doc.images[image_file_name] = [image, name]
|
1651
|
+
[image, name]
|
1652
|
+
end
|
1653
|
+
|
1654
|
+
def print_image_file(image_file_name, x=nil, y=nil, width=nil, height=nil)
|
1655
|
+
image, name = load_image(image_file_name)
|
1656
|
+
x ||= pen_pos.x
|
1657
|
+
y ||= pen_pos.y
|
1658
|
+
if width.nil? and height.nil?
|
1659
|
+
width, height = image.width, image.height
|
1660
|
+
elsif width.nil?
|
1661
|
+
height = to_points(@units, height)
|
1662
|
+
width = height * image.width.quo(image.height)
|
1663
|
+
elsif height.nil?
|
1664
|
+
width = to_points(@units, width)
|
1665
|
+
height = width * image.height.quo(image.width)
|
1666
|
+
else
|
1667
|
+
width, height = to_points(@units, width), to_points(@units, height)
|
1668
|
+
end
|
1669
|
+
end_path if @in_path
|
1670
|
+
gw.save_graphics_state
|
1671
|
+
gw.concat_matrix(width, 0, 0, height, to_points(@units, x), to_points(@units, page_height - y) - height)
|
1672
|
+
mw.x_object(name)
|
1673
|
+
gw.restore_graphics_state
|
1674
|
+
[from_points(@units, width), from_points(@units, height)]
|
1675
|
+
end
|
1676
|
+
|
1677
|
+
def print_image(data, x=nil, y=nil, width=nil, height=nil)
|
1678
|
+
image_file_name = data.hash.to_s
|
1679
|
+
image, name = load_image(image_file_name, data)
|
1680
|
+
print_image_file(image_file_name, x, y, width, height)
|
1681
|
+
end
|
1682
|
+
|
1683
|
+
def print_link(s, uri)
|
1684
|
+
# TODO
|
1685
|
+
end
|
1686
|
+
|
1687
|
+
def bullet(name, options={}, &block)
|
1688
|
+
return nil if name.nil?
|
1689
|
+
return @doc.bullets[name.to_sym] unless block_given?
|
1690
|
+
if width = options[:width]
|
1691
|
+
units = options[:units] || self.units
|
1692
|
+
width = to_points(units, width)
|
1693
|
+
end
|
1694
|
+
@doc.bullets[name.to_sym] = Bullet.new(name.to_s, width || 36, block)
|
1695
|
+
end
|
1696
|
+
|
1697
|
+
def rotate(angle, x, y, &block)
|
1698
|
+
return unless block_given?
|
1699
|
+
end_path if @in_path
|
1700
|
+
check_set(:line_color, :line_width, :line_dash_pattern, :fill_color)
|
1701
|
+
|
1702
|
+
theta = angle.degrees
|
1703
|
+
v_cos = Math::cos(theta)
|
1704
|
+
v_sin = Math::sin(theta)
|
1705
|
+
|
1706
|
+
xx, xy = to_points(@units, x), @page_height - to_points(@units, y)
|
1707
|
+
rot_x, rot_y = rotate_xy_coordinate(xx, xy, angle)
|
1708
|
+
gw.save_graphics_state
|
1709
|
+
gw.concat_matrix(v_cos, v_sin, -v_sin, v_cos, xx - rot_x, xy - rot_y)
|
1710
|
+
@last_page_font = nil
|
1711
|
+
yield
|
1712
|
+
gw.restore_graphics_state
|
1713
|
+
@last_page_font = nil
|
1714
|
+
end
|
1715
|
+
|
1716
|
+
def scale(x, y, scale_x, scale_y, &block)
|
1717
|
+
return unless x && scale_x && y && scale_y && block_given?
|
1718
|
+
end_path if @in_path
|
1719
|
+
check_set(:line_color, :line_width, :line_dash_pattern, :fill_color)
|
1720
|
+
sub_area = IDENTITY_MATRIX.dup
|
1721
|
+
# a: Sx (horizontal scaling, 1 unit in new is x units in old)
|
1722
|
+
# b:
|
1723
|
+
# c:
|
1724
|
+
# d: Sy (vertical scaling, 1 unit in new is y units in old)
|
1725
|
+
# e: Tx (horizontal translation of the origin)
|
1726
|
+
# f: Ty (vertical translation of the origin)
|
1727
|
+
sub_area[0] = scale_x
|
1728
|
+
sub_area[3] = scale_y
|
1729
|
+
sub_area[4] = to_points(@units, x)
|
1730
|
+
sub_area[5] = to_points(@units, -y)
|
1731
|
+
save_page_height = @page_height
|
1732
|
+
@page_height = save_page_height.quo(scale_y)
|
1733
|
+
gw.save_graphics_state
|
1734
|
+
gw.concat_matrix(*sub_area)
|
1735
|
+
@last_page_font = nil
|
1736
|
+
yield
|
1737
|
+
end_text if @in_text
|
1738
|
+
end_graph if @in_graph
|
1739
|
+
gw.restore_graphics_state
|
1740
|
+
@last_page_font = nil
|
1741
|
+
@page_height = save_page_height
|
1742
|
+
end
|
1743
|
+
|
1744
|
+
def text_encoding(value=nil)
|
1745
|
+
return @text_encoding if value.nil?
|
1746
|
+
@text_encoding = value
|
1747
|
+
end
|
1748
|
+
end
|
1749
|
+
end
|