prawn 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. data/COPYING +340 -0
  2. data/LICENSE +56 -0
  3. data/README +30 -0
  4. data/Rakefile +83 -0
  5. data/data/fonts/Activa.ttf +0 -0
  6. data/data/fonts/Chalkboard.ttf +0 -0
  7. data/data/fonts/Courier-Bold.afm +342 -0
  8. data/data/fonts/Courier-BoldOblique.afm +342 -0
  9. data/data/fonts/Courier-Oblique.afm +342 -0
  10. data/data/fonts/Courier.afm +342 -0
  11. data/data/fonts/DejaVuSans.ttf +0 -0
  12. data/data/fonts/Dustismo_Roman.ttf +0 -0
  13. data/data/fonts/Helvetica-Bold.afm +2827 -0
  14. data/data/fonts/Helvetica-BoldOblique.afm +2827 -0
  15. data/data/fonts/Helvetica-Oblique.afm +3051 -0
  16. data/data/fonts/Helvetica.afm +3051 -0
  17. data/data/fonts/MustRead.html +19 -0
  18. data/data/fonts/Symbol.afm +213 -0
  19. data/data/fonts/Times-Bold.afm +2588 -0
  20. data/data/fonts/Times-BoldItalic.afm +2384 -0
  21. data/data/fonts/Times-Italic.afm +2667 -0
  22. data/data/fonts/Times-Roman.afm +2419 -0
  23. data/data/fonts/ZapfDingbats.afm +225 -0
  24. data/data/fonts/comicsans.ttf +0 -0
  25. data/data/fonts/gkai00mp.ttf +0 -0
  26. data/data/images/dice.png +0 -0
  27. data/data/images/pigs.jpg +0 -0
  28. data/data/images/ruport.png +0 -0
  29. data/data/images/ruport_data.dat +0 -0
  30. data/data/images/ruport_transparent.png +0 -0
  31. data/data/images/stef.jpg +0 -0
  32. data/data/shift_jis_text.txt +1 -0
  33. data/examples/addressbook.csv +6 -0
  34. data/examples/alignment.rb +16 -0
  35. data/examples/bounding_boxes.pdf +62 -0
  36. data/examples/bounding_boxes.rb +30 -0
  37. data/examples/canvas.pdf +81 -0
  38. data/examples/canvas.rb +12 -0
  39. data/examples/cell.rb +27 -0
  40. data/examples/currency.csv +1834 -0
  41. data/examples/curves.rb +10 -0
  42. data/examples/fancy_table.rb +48 -0
  43. data/examples/font_size.rb +19 -0
  44. data/examples/hexagon.rb +14 -0
  45. data/examples/image.pdf +0 -0
  46. data/examples/image.rb +23 -0
  47. data/examples/image2.rb +13 -0
  48. data/examples/inline_styles.pdf +117 -0
  49. data/examples/kerning.rb +27 -0
  50. data/examples/line.rb +31 -0
  51. data/examples/multi_page_layout.rb +14 -0
  52. data/examples/on_page_start.rb +17 -0
  53. data/examples/page_geometry.rb +28 -0
  54. data/examples/polygons.rb +16 -0
  55. data/examples/ruport_formatter.rb +47 -0
  56. data/examples/ruport_helpers.rb +17 -0
  57. data/examples/russian_boxes.rb +34 -0
  58. data/examples/simple_text.rb +15 -0
  59. data/examples/simple_text_ttf.rb +16 -0
  60. data/examples/sjis.rb +19 -0
  61. data/examples/table.rb +45 -0
  62. data/examples/table_bench.rb +92 -0
  63. data/examples/text_flow.rb +65 -0
  64. data/examples/utf8.rb +12 -0
  65. data/lib/prawn.rb +33 -0
  66. data/lib/prawn/compatibility.rb +33 -0
  67. data/lib/prawn/document.rb +334 -0
  68. data/lib/prawn/document/bounding_box.rb +253 -0
  69. data/lib/prawn/document/page_geometry.rb +78 -0
  70. data/lib/prawn/document/table.rb +253 -0
  71. data/lib/prawn/document/text.rb +346 -0
  72. data/lib/prawn/errors.rb +33 -0
  73. data/lib/prawn/font.rb +5 -0
  74. data/lib/prawn/font/cmap.rb +59 -0
  75. data/lib/prawn/font/metrics.rb +414 -0
  76. data/lib/prawn/font/wrapping.rb +45 -0
  77. data/lib/prawn/graphics.rb +285 -0
  78. data/lib/prawn/graphics/cell.rb +226 -0
  79. data/lib/prawn/images.rb +241 -0
  80. data/lib/prawn/images/jpg.rb +43 -0
  81. data/lib/prawn/images/png.rb +178 -0
  82. data/lib/prawn/pdf_object.rb +64 -0
  83. data/lib/prawn/reference.rb +47 -0
  84. data/spec/bounding_box_spec.rb +120 -0
  85. data/spec/box_calculation_spec.rb +17 -0
  86. data/spec/document_spec.rb +152 -0
  87. data/spec/graphics_spec.rb +250 -0
  88. data/spec/images_spec.rb +42 -0
  89. data/spec/jpg_spec.rb +25 -0
  90. data/spec/metrics_spec.rb +60 -0
  91. data/spec/pdf_object_spec.rb +102 -0
  92. data/spec/png_spec.rb +35 -0
  93. data/spec/reference_spec.rb +29 -0
  94. data/spec/spec_helper.rb +29 -0
  95. data/spec/table_spec.rb +145 -0
  96. data/spec/text_spec.rb +190 -0
  97. data/vendor/font_ttf/ttf.rb +20 -0
  98. data/vendor/font_ttf/ttf/datatypes.rb +189 -0
  99. data/vendor/font_ttf/ttf/encodings.rb +140 -0
  100. data/vendor/font_ttf/ttf/exceptions.rb +28 -0
  101. data/vendor/font_ttf/ttf/file.rb +290 -0
  102. data/vendor/font_ttf/ttf/fontchunk.rb +77 -0
  103. data/vendor/font_ttf/ttf/table/cmap.rb +408 -0
  104. data/vendor/font_ttf/ttf/table/cvt.rb +49 -0
  105. data/vendor/font_ttf/ttf/table/fpgm.rb +48 -0
  106. data/vendor/font_ttf/ttf/table/gasp.rb +88 -0
  107. data/vendor/font_ttf/ttf/table/glyf.rb +452 -0
  108. data/vendor/font_ttf/ttf/table/head.rb +86 -0
  109. data/vendor/font_ttf/ttf/table/hhea.rb +96 -0
  110. data/vendor/font_ttf/ttf/table/hmtx.rb +98 -0
  111. data/vendor/font_ttf/ttf/table/kern.rb +186 -0
  112. data/vendor/font_ttf/ttf/table/loca.rb +75 -0
  113. data/vendor/font_ttf/ttf/table/maxp.rb +81 -0
  114. data/vendor/font_ttf/ttf/table/name.rb +222 -0
  115. data/vendor/font_ttf/ttf/table/os2.rb +172 -0
  116. data/vendor/font_ttf/ttf/table/post.rb +120 -0
  117. data/vendor/font_ttf/ttf/table/prep.rb +27 -0
  118. data/vendor/font_ttf/ttf/table/vhea.rb +45 -0
  119. data/vendor/font_ttf/ttf/table/vmtx.rb +36 -0
  120. metadata +180 -0
@@ -0,0 +1,49 @@
1
+ # TTF/Ruby, a library to read and write TrueType fonts in Ruby.
2
+ # Copyright (C) 2006 Mathieu Blondel
3
+ #
4
+ # This program is free software; you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation; either version 2 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17
+
18
+ module Font
19
+ module TTF
20
+ module Table
21
+
22
+ # Cvt is the Control Value table. It references values that can be
23
+ # referenced by instructions (used for hinting, aka grid-fitting).
24
+ class Cvt < Font::TTF::FontChunk
25
+
26
+ attr_accessor :instructions
27
+
28
+ def initialize(*args)
29
+ super(*args)
30
+
31
+ if exists_in_file?
32
+ @font.at_offset(@offset) do
33
+ n = @len / IO::SIZEOF_FWORD
34
+ @instructions = @font.read_fwords(n)
35
+ end
36
+ end
37
+ end
38
+
39
+ # Dumps the cvt table in binary raw format as may be found in a font
40
+ # file.
41
+ def dump
42
+ (@instructions || []).to_fwords
43
+ end
44
+
45
+ end
46
+
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,48 @@
1
+ # TTF/Ruby, a library to read and write TrueType fonts in Ruby.
2
+ # Copyright (C) 2006 Mathieu Blondel
3
+ #
4
+ # This program is free software; you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation; either version 2 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17
+
18
+ module Font
19
+ module TTF
20
+ module Table
21
+
22
+ # Fpgm is the Font Program table. It is similar to the Cvt table except
23
+ # it is only run once, when the font is first used.
24
+ class Fpgm < Font::TTF::FontChunk
25
+
26
+ attr_accessor :instructions
27
+
28
+ def initialize(*args)
29
+ super(*args)
30
+
31
+ if exists_in_file?
32
+ @font.at_offset(@offset) do
33
+ @instructions = @font.read_bytes(@len)
34
+ end
35
+ end
36
+ end
37
+
38
+ # Dumps the fpgm table in binary raw format as may be found in a font
39
+ # file.
40
+ def dump
41
+ (@instructions || []).to_bytes
42
+ end
43
+
44
+ end
45
+
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,88 @@
1
+ # TTF/Ruby, a library to read and write TrueType fonts in Ruby.
2
+ # Copyright (C) 2006 Mathieu Blondel
3
+ #
4
+ # This program is free software; you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation; either version 2 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17
+
18
+ module Font
19
+ module TTF
20
+ module Table
21
+
22
+ # Gasp is the Grid-fitting and scan conversion procedure table.
23
+ class Gasp < Font::TTF::FontChunk
24
+
25
+ class GaspRange < Font::TTF::FontChunk
26
+
27
+ SIZEOF_GASP_RANGE = 2 * IO::SIZEOF_USHORT
28
+
29
+ attr_accessor :range_max_ppem, :range_gasp_behavior
30
+
31
+ def initialize(table, n=nil)
32
+ if n.nil?
33
+ # when object is created by hand
34
+ super(table.font)
35
+ else
36
+ offs = table.offset + 2 * IO::SIZEOF_USHORT + \
37
+ n * SIZEOF_GASP_RANGE
38
+ super(table.font, offs, SIZEOF_GASP_RANGE)
39
+
40
+ table.font.at_offset(@offset) do
41
+ @range_max_ppem = table.font.read_ushort
42
+ @range_gasp_behavior = table.font.read_ushort
43
+ end
44
+ end
45
+ end
46
+
47
+ def dump
48
+ raw = (@range_max_ppem || 0).to_ushort
49
+ raw += (@range_gasp_behavior || 0).to_ushort
50
+ end
51
+
52
+ end
53
+
54
+ attr_accessor :version
55
+ # An Array of GaspRange objects.
56
+ attr_accessor :gasp_ranges
57
+
58
+ def initialize(*args)
59
+ super(*args)
60
+
61
+ if exists_in_file?
62
+ @font.at_offset(@offset) do
63
+ @version = @font.read_ushort
64
+ @num_ranges = @font.read_ushort
65
+ @gasp_ranges = []
66
+ @num_ranges.times do |i|
67
+ @gasp_ranges << GaspRange.new(self, i)
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ # Dumps the gasp table in binary raw format as may be found in a font
74
+ # file.
75
+ def dump
76
+ raw = (@version || 0).to_ushort
77
+ raw += (@gasp_ranges || []).length.to_ushort
78
+ @gasp_ranges.each do |gr|
79
+ raw += gr.dump
80
+ end
81
+ raw
82
+ end
83
+
84
+ end
85
+
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,452 @@
1
+ # TTF/Ruby, a library to read and write TrueType fonts in Ruby.
2
+ # Copyright (C) 2006 Mathieu Blondel
3
+ #
4
+ # This program is free software; you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation; either version 2 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17
+
18
+ module Font
19
+ module TTF
20
+ module Table
21
+
22
+ # Glyf is the Glyph data table.
23
+ class Glyf < Font::TTF::FontChunk
24
+
25
+ # Base class for SimpleGlyph and CompositeGlyph.
26
+ class Glyph < Font::TTF::FontChunk
27
+
28
+ attr_accessor :num_contours, :x_min, :y_min, :x_max, :y_max
29
+
30
+ def initialize(table, offset=nil)
31
+ @table = table
32
+ @offset_from_table = offset
33
+ super(@table.font, @table.offset + @offset_from_table)
34
+
35
+ if exists_in_file?
36
+ @font.at_offset(@offset) do
37
+ @num_contours = @font.read_short
38
+ @x_min = @font.read_fword
39
+ @y_min = @font.read_fword
40
+ @x_max = @font.read_fword
41
+ @y_max = @font.read_fword
42
+ end
43
+ end
44
+ end
45
+
46
+ def dump
47
+ raw = (@num_contours || 0).to_short
48
+ raw += (@x_min || 0).to_fword
49
+ raw += (@y_min || 0).to_fword
50
+ raw += (@x_max || 0).to_fword
51
+ raw += (@y_max || 0).to_fword
52
+ end
53
+
54
+ # Returns whether the glyph is composite or not.
55
+ def composite?
56
+ self.class == CompositeGlyph
57
+ end
58
+
59
+ # Returns whether is simple (i.e. not composite) or not.
60
+ def simple?
61
+ self.class == SimpleGlyph
62
+ end
63
+
64
+ end
65
+
66
+ # SimpleGlyph class.
67
+ class SimpleGlyph < Glyph
68
+
69
+ Point = Struct.new(:rel_x, :abs_x, :rel_y, :abs_y,
70
+ :end_of_contour, :on_curve)
71
+
72
+ # Point is helper class which gives information on a point
73
+ # such as it absolute (abs_x, abs_y) and relative (rel_x, rel_y)
74
+ # coordinates
75
+ class Point
76
+ # Whether the point is end of contour or not.
77
+ alias :end_of_contour? :end_of_contour
78
+ # Whether the point is on curve or not.
79
+ alias :on_curve? :on_curve
80
+
81
+ # Whether the point is off curve or not.
82
+ def off_curve?
83
+ not on_curve?
84
+ end
85
+ end
86
+
87
+ FLAG_ON_CURVE = 0b1
88
+ FLAG_X_SHORT_VECTOR = 0b10
89
+ FLAG_Y_SHORT_VECTOR = 0b100
90
+ FLAG_REPEAT = 0b1000
91
+ FLAG_X_IS_SAME = 0b10000
92
+ FLAG_Y_IS_SAME = 0b100000
93
+
94
+ attr_accessor :end_pts_of_contours, :instructions, :flags,
95
+ :x_coordinates, :y_coordinates
96
+
97
+ def initialize(*args)
98
+ super(*args)
99
+
100
+ if exists_in_file?
101
+ offs = @offset + IO::SIZEOF_SHORT + 4 * IO::SIZEOF_FWORD
102
+ @font.at_offset(offs) do
103
+ @end_pts_of_contours = @font.read_ushorts(@num_contours)
104
+ instruction_len = @font.read_ushort
105
+ @instructions = @font.read_bytes(instruction_len)
106
+ unless @end_pts_of_contours.empty?
107
+ num_points = @end_pts_of_contours.last + 1
108
+ else
109
+ num_points = 0
110
+ end
111
+ @flags = []
112
+ while @flags.length < num_points
113
+ flag = @font.read_byte
114
+ @flags << flag
115
+ if flag & FLAG_REPEAT != 0
116
+ @font.read_byte.times do
117
+ @flags << flag
118
+ end
119
+ end
120
+ end
121
+ @x_coordinates = []
122
+ @y_coordinates = []
123
+ [[@x_coordinates, FLAG_X_SHORT_VECTOR, FLAG_X_IS_SAME],
124
+ [@y_coordinates, FLAG_Y_SHORT_VECTOR, FLAG_Y_IS_SAME]
125
+ ].each do |coordinates, short, same|
126
+ num_points.times do |i|
127
+ flag = @flags[i]
128
+ if flag & short != 0
129
+ # if the coordinate is a BYTE
130
+ if flag & same != 0
131
+ coordinates << @font.read_byte
132
+ else
133
+ coordinates << -@font.read_byte
134
+ end
135
+ else
136
+ # the coordinate is a SHORT
137
+ if flag & same != 0
138
+ # same so 0 (relative coordinates)
139
+ coordinates << 0
140
+ else
141
+ coordinates << @font.read_short
142
+ end
143
+ end
144
+ end
145
+ end
146
+ @len = @font.pos - @offset
147
+ end
148
+ end
149
+
150
+ end
151
+
152
+ # Returns an Array of [x,y] pairs of relative coordinates.
153
+ def rel_coordinates
154
+ coords = []
155
+ @x_coordinates.length.times do |i|
156
+ coords << [@x_coordinates[i], @y_coordinates[i]]
157
+ end
158
+ coords
159
+ end
160
+
161
+ # Returns an Array of [x,y] pairs of absolute coordinates.
162
+ def abs_coordinates
163
+ abs_x = 0
164
+ abs_y = 0
165
+ coords = []
166
+ rel_coordinates.each do |rel_x, rel_y|
167
+ abs_x += rel_x
168
+ abs_y += rel_y
169
+ coords << [abs_x, abs_y]
170
+ end
171
+ coords
172
+ end
173
+
174
+ # Returns an Array of Point objects.
175
+ def points
176
+ x_abs = 0
177
+ y_abs = 0
178
+ pnts = []
179
+ @x_coordinates.length.times do |i|
180
+ pnt = Point.new
181
+ pnt.rel_x = @x_coordinates[i]
182
+ pnt.rel_y = @y_coordinates[i]
183
+ x_abs += pnt.rel_x
184
+ y_abs += pnt.rel_y
185
+ pnt.abs_x = x_abs
186
+ pnt.abs_y = y_abs
187
+ pnt.end_of_contour = @end_pts_of_contours.include? i
188
+ pnt.on_curve = (@flags[i] & FLAG_ON_CURVE != 0)
189
+ pnts << pnt
190
+ end
191
+ pnts
192
+ end
193
+
194
+ def dump
195
+ raw = super
196
+ raw += @end_pts_of_contours.to_ushorts
197
+ raw += @instructions.length.to_ushort
198
+ raw += @instructions.to_bytes
199
+
200
+ tmp = ""
201
+ [[@x_coordinates, FLAG_X_SHORT_VECTOR, FLAG_X_IS_SAME],
202
+ [@y_coordinates, FLAG_Y_SHORT_VECTOR, FLAG_Y_IS_SAME]
203
+ ].each do |coordinates, short, same|
204
+ coordinates.each_with_index do |coord, i|
205
+ if 0 <= coord and coord <= 255
206
+ @flags[i] = (@flags[i] | short) | same
207
+ tmp += coord.to_byte
208
+ elsif -255 <= coord and coord < 0
209
+ @flags[i] = (@flags[i] | short) & ~same
210
+ tmp += (-coord).to_byte
211
+ elsif coord == 0
212
+ @flags[i] = (@flags[i] & ~short) | same
213
+ else
214
+ @flags[i] = (@flags[i] & ~short) & ~same
215
+ tmp += coord.to_short
216
+ end
217
+ end
218
+ end
219
+
220
+ # We write all flags rather than using the flag_repeat trick
221
+ # So we unset the "repeat" bit for all flags
222
+ # TODO: implement the repeat feature (this saves space)
223
+ raw += @flags.collect { |f| f & ~FLAG_REPEAT }.to_bytes
224
+
225
+ raw += tmp
226
+ end
227
+ end
228
+
229
+ # CompositeGlyph class.
230
+ class CompositeGlyph < Glyph
231
+
232
+ GlyphComponent = Struct.new(:flags, :index, :args, :scale,
233
+ :xscale, :yscale, :scale01, :scale10)
234
+
235
+ ARG_1_AND_2_ARE_WORDS = 0b1
236
+ ARGS_ARE_XY_VALUES = 0b10
237
+ ROUND_XY_TO_GRID = 0b100
238
+ WE_HAVE_A_SCALE = 0b1000
239
+ RESERVED = 0b10000
240
+ MORE_COMPONENTS = 0b100000
241
+ WE_HAVE_AN_X_AND_Y_SCALE = 0b1000000
242
+ WE_HAVE_A_TWO_BY_TWO = 0b10000000
243
+ WE_HAVE_INSTRUCTIONS = 0b100000000
244
+ USE_MY_METRICS = 0b1000000000
245
+
246
+ # An Array of GlyphComponent objects
247
+ attr_accessor :components
248
+ # An Array of instructions (Fixnums)
249
+ attr_accessor :instructions
250
+
251
+ def initialize(*args)
252
+ super(*args)
253
+
254
+ if exists_in_file?
255
+ offs = @offset + IO::SIZEOF_SHORT + 4 * IO::SIZEOF_FWORD
256
+ @font.at_offset(offs) do
257
+ @components = []
258
+ continue = true
259
+ while continue
260
+ gc = GlyphComponent.new
261
+ gc.flags = @font.read_ushort
262
+ gc.index = @font.read_ushort
263
+
264
+ gc.args = []
265
+ if gc.flags & ARG_1_AND_2_ARE_WORDS != 0
266
+ gc.args[0] = @font.read_short
267
+ gc.args[1] = @font.read_short
268
+ else
269
+ gc.args[0] = @font.read_ushort
270
+ end
271
+
272
+ if gc.flags & WE_HAVE_A_SCALE != 0
273
+ gc.scale = @font.read_f2dot14
274
+ elsif gc.flags & WE_HAVE_AN_X_AND_Y_SCALE != 0
275
+ gc.xscale = @font.read_f2dot14
276
+ gc.yscale = @font.read_f2dot14
277
+ elsif gc.flags & WE_HAVE_A_TWO_BY_TWO != 0
278
+ gc.xscale = @font.read_f2dot14
279
+ gc.scale01 = @font.read_f2dot14
280
+ gc.scale10 = @font.read_f2dot14
281
+ gc.yscale = @font.read_f2dot14
282
+ end
283
+ @components << gc
284
+ continue = (gc.flags & MORE_COMPONENTS != 0)
285
+ end
286
+
287
+ if @components.last.flags & \
288
+ WE_HAVE_INSTRUCTIONS != 0
289
+ inst_len = @font.read_ushort
290
+ @instructions = @font.read_bytes(inst_len)
291
+ else
292
+ @instructions = []
293
+ end
294
+
295
+ @len = @font.pos - @offset
296
+ end
297
+ end
298
+ end
299
+
300
+ def dump
301
+ raw = super
302
+ components_len = @components.length
303
+ @components.each_with_index do |gc, i|
304
+ flags = gc.flags
305
+ tmp = ""
306
+ if not gc.args.nil? and gc.args.length == 2
307
+ flags |= ARG_1_AND_2_ARE_WORDS
308
+ tmp += gc.args[0].to_short
309
+ tmp += gc.args[1].to_short
310
+ else
311
+ flags &= ~ARG_1_AND_2_ARE_WORDS
312
+ tmp += gc.args[0].to_ushort
313
+ end
314
+ if not gc.scale.nil?
315
+ flags |= WE_HAVE_A_SCALE
316
+ tmp += gc.scale.to_f2dot14
317
+ elsif not gc.scale01.nil?
318
+ flags |= WE_HAVE_A_TWO_BY_TWO
319
+ tmp += gc.xscale.to_f2dot14
320
+ tmp += gc.scale01.to_f2dot14
321
+ tmp += gc.scale10.to_f2dot14
322
+ tmp += gc.yscale.to_f2dot14
323
+ elsif not gc.xscale.nil?
324
+ flags |= WE_HAVE_AN_X_AND_Y_SCALE
325
+ tmp += gc.xscale.to_f2dot14
326
+ tmp += gc.yscale.to_f2dot14
327
+ end
328
+
329
+ if i < components_len - 1
330
+ flags |= MORE_COMPONENTS
331
+ else
332
+ flags &= ~MORE_COMPONENTS
333
+ if @instructions.length > 0
334
+ flags |= WE_HAVE_INSTRUCTIONS
335
+ else
336
+ flags &= ~WE_HAVE_INSTRUCTIONS
337
+ end
338
+ end
339
+ raw += flags.to_ushort
340
+ raw += gc.index.to_ushort
341
+ raw += tmp
342
+ end
343
+ unless @instructions.empty?
344
+ raw += @instructions.length.to_ushort
345
+ raw += @instructions.to_bytes
346
+ end
347
+ raw
348
+ end
349
+ end
350
+
351
+ attr_accessor :glyphs
352
+
353
+ def initialize(*args)
354
+ super(*args)
355
+ end
356
+
357
+ # Returns the kind of glyph at offset, i.e. either SimpleGlyph
358
+ # or CompositeGlyph.
359
+ def kind_of_glyph_at_offset(offs_from_table)
360
+ @font.at_offset(@offset + offs_from_table) do
361
+ num_contours = @font.read_short
362
+ if num_contours >= 0
363
+ SimpleGlyph
364
+ else
365
+ CompositeGlyph
366
+ end
367
+ end
368
+ end
369
+
370
+ def get_glyph_at_offset(offs_from_table)
371
+ klass = kind_of_glyph_at_offset(offs_from_table)
372
+ klass.new(self, offs_from_table)
373
+ end
374
+
375
+ def get_glyphs
376
+ loca = @font.get_table(:loca)
377
+ glyphs = []
378
+ loca.glyph_offsets[0...-1].each do |off|
379
+ glyphs << get_glyph_at_offset(off)
380
+ end
381
+ glyphs
382
+ end
383
+ private :get_glyphs
384
+
385
+ # Returns all Glyph (SimpleGlyph or CompositeGlyph) in an Array.
386
+ # This method may be real overkill if you just need to access a few glyphs.
387
+ # In this case, you should use the loca table (Font::TTF::Table::Loca)
388
+ # to get offsets and get_glyph_at_offset to get glyph associated with them.
389
+ def glyphs
390
+ @glyphs ||= get_glyphs
391
+ end
392
+
393
+ # Sets glyphs. new_glyphs is an Array of Glyph objects.
394
+ def glyphs=(new_glyphs)
395
+ @glyphs = new_glyphs
396
+ @font.get_table(:maxp).num_glyphs = @glyphs.length
397
+ @font.get_table(:post).num_glyphs = @glyphs.length
398
+ end
399
+
400
+ # Iterates over each glyph.
401
+ # It does not load all glyphs like glyphs.each would do.
402
+ def each_glyph
403
+ loca = @font.get_table(:loca)
404
+ glyphs = []
405
+ loca.glyph_offsets[0...-1].each do |off|
406
+ glyph = get_glyph_at_offset(off)
407
+ yield glyph
408
+ end
409
+ end
410
+
411
+ # Dumps the glyf table in binary raw format as may be found in a font
412
+ # file.
413
+ def dump
414
+ raw = ""
415
+ offs = 0
416
+ glyph_offsets = []
417
+ glyphs.each do |glyph|
418
+ glyph_offsets << offs
419
+ dump = glyph.dump
420
+ len = dump.length
421
+ raw += dump
422
+ # offsets should be multiples of SIZEOF_ULONG
423
+ diff = len % IO::SIZEOF_ULONG
424
+ raw += " " * diff
425
+ offs += len + diff
426
+ end
427
+ # An additional offset is added so that the length of the last
428
+ # glyph can be calculated:
429
+ # len of last glyph = additional offs - last glyph offs
430
+ glyph_offsets << offs
431
+
432
+ # 2 ** 16 * 2 = 131072 is the maximum size supported
433
+ # if the short format is used in the loca table
434
+ # Using shorts saves two bytes per glyph!
435
+ if offs < 131072
436
+ @font.get_table(:head).index_to_loc_format = \
437
+ Font::TTF::Table::Head::SHORT_FORMAT
438
+ else
439
+ @font.get_table(:head).index_to_loc_format = \
440
+ Font::TTF::Table::Head::LONG_FORMAT
441
+ end
442
+
443
+ @font.get_table(:loca).glyph_offsets = glyph_offsets
444
+
445
+ raw
446
+ end
447
+
448
+ end
449
+
450
+ end
451
+ end
452
+ end