prawn 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. data/Rakefile +3 -1
  2. data/data/fonts/Action Man.dfont +0 -0
  3. data/examples/general/measurement_units.rb +2 -2
  4. data/examples/graphics/image_flow.rb +2 -2
  5. data/examples/graphics/stroke_bounds.rb +1 -1
  6. data/examples/m17n/win_ansi_charset.rb +3 -3
  7. data/examples/text/dfont.rb +49 -0
  8. data/examples/text/flowing_text_with_header_and_footer.rb +2 -48
  9. data/examples/text/font_calculations.rb +7 -6
  10. data/examples/text/font_size.rb +4 -4
  11. data/examples/text/text_flow.rb +1 -1
  12. data/lib/prawn.rb +6 -3
  13. data/lib/prawn/compatibility.rb +12 -17
  14. data/lib/prawn/document.rb +10 -10
  15. data/lib/prawn/document/internals.rb +8 -3
  16. data/lib/prawn/document/text.rb +39 -57
  17. data/lib/prawn/document/text/box.rb +1 -2
  18. data/lib/prawn/document/text/wrapping.rb +59 -0
  19. data/lib/prawn/errors.rb +0 -8
  20. data/lib/prawn/font.rb +192 -277
  21. data/lib/prawn/font/afm.rb +199 -0
  22. data/lib/prawn/font/dfont.rb +31 -0
  23. data/lib/prawn/font/ttf.rb +318 -0
  24. data/lib/prawn/graphics.rb +7 -2
  25. data/lib/prawn/images/png.rb +1 -1
  26. data/lib/prawn/reference.rb +7 -4
  27. data/spec/font_spec.rb +154 -61
  28. data/spec/text_spec.rb +47 -6
  29. data/vendor/pdf-inspector/lib/pdf/inspector.rb +1 -1
  30. data/vendor/ttfunk/example.rb +42 -2
  31. data/vendor/ttfunk/lib/ttfunk.rb +96 -42
  32. data/vendor/ttfunk/lib/ttfunk/directory.rb +17 -0
  33. data/vendor/ttfunk/lib/ttfunk/encoding/mac_roman.rb +88 -0
  34. data/vendor/ttfunk/lib/ttfunk/encoding/windows_1252.rb +69 -0
  35. data/vendor/ttfunk/lib/ttfunk/reader.rb +44 -0
  36. data/vendor/ttfunk/lib/ttfunk/resource_file.rb +78 -0
  37. data/vendor/ttfunk/lib/ttfunk/subset.rb +18 -0
  38. data/vendor/ttfunk/lib/ttfunk/subset/base.rb +141 -0
  39. data/vendor/ttfunk/lib/ttfunk/subset/mac_roman.rb +46 -0
  40. data/vendor/ttfunk/lib/ttfunk/subset/unicode.rb +48 -0
  41. data/vendor/ttfunk/lib/ttfunk/subset/unicode_8bit.rb +63 -0
  42. data/vendor/ttfunk/lib/ttfunk/subset/windows_1252.rb +51 -0
  43. data/vendor/ttfunk/lib/ttfunk/subset_collection.rb +72 -0
  44. data/vendor/ttfunk/lib/ttfunk/table.rb +37 -18
  45. data/vendor/ttfunk/lib/ttfunk/table/cmap.rb +24 -84
  46. data/vendor/ttfunk/lib/ttfunk/table/cmap/format00.rb +54 -0
  47. data/vendor/ttfunk/lib/ttfunk/table/cmap/format04.rb +126 -0
  48. data/vendor/ttfunk/lib/ttfunk/table/cmap/subtable.rb +79 -0
  49. data/vendor/ttfunk/lib/ttfunk/table/glyf.rb +64 -0
  50. data/vendor/ttfunk/lib/ttfunk/table/glyf/compound.rb +81 -0
  51. data/vendor/ttfunk/lib/ttfunk/table/glyf/simple.rb +37 -0
  52. data/vendor/ttfunk/lib/ttfunk/table/head.rb +38 -19
  53. data/vendor/ttfunk/lib/ttfunk/table/hhea.rb +35 -21
  54. data/vendor/ttfunk/lib/ttfunk/table/hmtx.rb +40 -13
  55. data/vendor/ttfunk/lib/ttfunk/table/kern.rb +69 -38
  56. data/vendor/ttfunk/lib/ttfunk/table/kern/format0.rb +62 -0
  57. data/vendor/ttfunk/lib/ttfunk/table/loca.rb +43 -0
  58. data/vendor/ttfunk/lib/ttfunk/table/maxp.rb +34 -11
  59. data/vendor/ttfunk/lib/ttfunk/table/name.rb +109 -42
  60. data/vendor/ttfunk/lib/ttfunk/table/os2.rb +78 -0
  61. data/vendor/ttfunk/lib/ttfunk/table/post.rb +91 -0
  62. data/vendor/ttfunk/lib/ttfunk/table/post/format10.rb +43 -0
  63. data/vendor/ttfunk/lib/ttfunk/table/post/format20.rb +35 -0
  64. data/vendor/ttfunk/lib/ttfunk/table/post/format25.rb +23 -0
  65. data/vendor/ttfunk/lib/ttfunk/table/post/format30.rb +17 -0
  66. data/vendor/ttfunk/lib/ttfunk/table/post/format40.rb +17 -0
  67. data/vendor/ttfunk/lib/ttfunk/table/simple.rb +14 -0
  68. metadata +54 -25
  69. data/examples/table/addressbook.csv +0 -6
  70. data/examples/table/cell.rb +0 -40
  71. data/examples/table/currency.csv +0 -1834
  72. data/examples/table/fancy_table.rb +0 -62
  73. data/examples/table/ruport_formatter.rb +0 -53
  74. data/examples/table/table.rb +0 -51
  75. data/examples/table/table_alignment.rb +0 -18
  76. data/examples/table/table_border_color.rb +0 -17
  77. data/examples/table/table_colspan.rb +0 -19
  78. data/examples/table/table_header_color.rb +0 -19
  79. data/examples/table/table_header_underline.rb +0 -15
  80. data/lib/prawn/document/table.rb +0 -338
  81. data/lib/prawn/font/cmap.rb +0 -59
  82. data/lib/prawn/font/metrics.rb +0 -378
  83. data/lib/prawn/font/wrapping.rb +0 -47
  84. data/lib/prawn/graphics/cell.rb +0 -264
  85. data/spec/metrics_spec.rb +0 -62
  86. data/spec/table_spec.rb +0 -179
  87. data/vendor/ttfunk/lib/ttfunk/table/directory.rb +0 -25
@@ -0,0 +1,81 @@
1
+ require 'ttfunk/reader'
2
+
3
+ module TTFunk
4
+ class Table
5
+ class Glyf
6
+ class Compound
7
+ include Reader
8
+
9
+ ARG_1_AND_2_ARE_WORDS = 0x0001
10
+ WE_HAVE_A_SCALE = 0x0008
11
+ MORE_COMPONENTS = 0x0020
12
+ WE_HAVE_AN_X_AND_Y_SCALE = 0x0040
13
+ WE_HAVE_A_TWO_BY_TWO = 0x0080
14
+ WE_HAVE_INSTRUCTIONS = 0x0100
15
+
16
+ attr_reader :raw
17
+ attr_reader :x_min, :y_min, :x_max, :y_max
18
+ attr_reader :glyph_ids
19
+
20
+ Component = Struct.new(:flags, :glyph_index, :arg1, :arg2, :transform)
21
+
22
+ def initialize(raw, x_min, y_min, x_max, y_max)
23
+ @raw = raw
24
+ @x_min, @y_min, @x_max, @y_max = x_min, y_min, x_max, y_max
25
+
26
+ # Because TTFunk only cares about glyphs insofar as they (1) provide
27
+ # a bounding box for each glyph, and (2) can be rewritten into a
28
+ # font subset, we don't really care about the rest of the glyph data
29
+ # except as a whole. Thus, we don't actually decompose the glyph
30
+ # into it's parts--all we really care about are the locations within
31
+ # the raw string where the component glyph ids are stored, so that
32
+ # when we rewrite this glyph into a subset we can rewrite the
33
+ # component glyph-ids so they are correct for the subset.
34
+
35
+ @glyph_ids = []
36
+ @glyph_id_offsets = []
37
+ offset = 10 # 2 bytes for each of num-contours, min x/y, max x/y
38
+
39
+ loop do
40
+ flags, glyph_id = @raw[offset, 4].unpack("n*")
41
+ @glyph_ids << glyph_id
42
+ @glyph_id_offsets << offset + 2
43
+
44
+ break unless flags & MORE_COMPONENTS != 0
45
+ offset += 4
46
+
47
+ if flags & ARG_1_AND_2_ARE_WORDS != 0
48
+ offset += 4
49
+ else
50
+ offset += 2
51
+ end
52
+
53
+ if flags & WE_HAVE_A_TWO_BY_TWO != 0
54
+ offset += 8
55
+ elsif flags & WE_HAVE_AN_X_AND_Y_SCALE != 0
56
+ offset += 4
57
+ elsif flags & WE_HAVE_A_SCALE != 0
58
+ offset += 2
59
+ end
60
+ end
61
+ end
62
+
63
+ def compound?
64
+ true
65
+ end
66
+
67
+ def recode(mapping)
68
+ result = @raw.dup
69
+ new_ids = glyph_ids.map { |id| mapping[id] }
70
+
71
+ new_ids.zip(@glyph_id_offsets).each do |new_id, offset|
72
+ result[offset, 2] = [new_id].pack("n")
73
+ end
74
+
75
+ return result
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+
@@ -0,0 +1,37 @@
1
+ require 'ttfunk/reader'
2
+
3
+ module TTFunk
4
+ class Table
5
+ class Glyf
6
+ class Simple
7
+ attr_reader :raw
8
+ attr_reader :number_of_contours
9
+ attr_reader :x_min, :y_min, :x_max, :y_max
10
+
11
+ def initialize(raw, number_of_contours, x_min, y_min, x_max, y_max)
12
+ @raw = raw
13
+ @number_of_contours = number_of_contours
14
+ @x_min, @y_min = x_min, y_min
15
+ @x_max, @y_max = x_max, y_max
16
+
17
+ # Because TTFunk is, at this time, a library for simply pulling
18
+ # metrics out of font files, or for writing font subsets, we don't
19
+ # really care what the contours are for simple glyphs. We just
20
+ # care that we've got an entire glyph's definition. Also, a
21
+ # bounding box could be nice to know. Since we've got all that
22
+ # at this point, we don't need to worry about parsing the full
23
+ # contents of the glyph.
24
+ end
25
+
26
+ def compound?
27
+ false
28
+ end
29
+
30
+ def recode(mapping)
31
+ raw
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+
@@ -1,25 +1,44 @@
1
+ require 'ttfunk/table'
2
+
1
3
  module TTFunk
2
4
  class Table
3
5
  class Head < TTFunk::Table
4
- def initialize(fh, font, info)
5
- fh.pos = info[:offset]
6
- data = fh.read(20)
7
- @version, @font_revision, @check_sum_adjustment, @magic_number,
8
- @flags, @units_per_em = data.unpack("N4n2")
9
-
10
- # skip dates
11
- fh.read(16)
12
-
13
- data = fh.read(8)
14
- @x_min, @y_min, @x_max, @y_max = data.unpack("n4").map { |e| to_signed(e) }
15
-
16
- data = fh.read(4)
17
- @mac_style, @lowest_rec_ppem = data.unpack("n2")
18
-
19
- data = fh.read(6)
20
- @font_direction_hint, @index_to_loc_format, @glyph_data_format =
21
- data.unpack("n3")
6
+ attr_reader :version
7
+ attr_reader :font_revision
8
+ attr_reader :checksum_adjustment
9
+ attr_reader :magic_number
10
+ attr_reader :flags
11
+ attr_reader :units_per_em
12
+ attr_reader :created
13
+ attr_reader :modified
14
+ attr_reader :x_min
15
+ attr_reader :y_min
16
+ attr_reader :x_max
17
+ attr_reader :y_max
18
+ attr_reader :mac_style
19
+ attr_reader :lowest_rec_ppem
20
+ attr_reader :font_direction_hint
21
+ attr_reader :index_to_loc_format
22
+ attr_reader :glyph_data_format
23
+
24
+ def self.encode(head, loca)
25
+ table = head.raw
26
+ table[8,4] = "\0\0\0\0" # set checksum adjustment to 0 initially
27
+ table[-4,2] = [loca[:type]].pack("n") # set index_to_loc_format
28
+ return table
22
29
  end
30
+
31
+ private
32
+
33
+ def parse!
34
+ @version, @font_revision, @check_sum_adjustment, @magic_number,
35
+ @flags, @units_per_em, @created, @modified = read(36, "N4n2q2")
36
+
37
+ @x_min, @y_min, @x_max, @y_max = read_signed(4)
38
+
39
+ @mac_style, @lowest_rec_ppem, @font_direction_hint,
40
+ @index_to_loc_format, @glyph_data_format = read(10, "n*")
41
+ end
23
42
  end
24
43
  end
25
- end
44
+ end
@@ -1,27 +1,41 @@
1
+ require 'ttfunk/table'
2
+
1
3
  module TTFunk
2
4
  class Table
3
5
  class Hhea < Table
4
- def initialize(fh, font, info)
5
- fh.pos = info[:offset]
6
- @length = info[:length]
7
- data = fh.read(4)
8
- @version = data.unpack("N")
9
-
10
- data = fh.read(6)
11
- @ascent, @descent, @line_gap= data.unpack("n3").map {|e| to_signed(e) }
12
-
13
- data = fh.read(2)
14
- @advance_width_max = data.unpack("n")
15
-
16
- data = fh.read(22)
17
- @min_left_side_bearing, @min_right_side_bearing, @x_max_extent,
18
- @caret_slope_rise, @caret_slope_run,
19
- @caret_offset, _, _, _, _, @metric_data_format =
20
- data.unpack("n11").map {|e| to_signed(e) }
21
-
22
- data = fh.read(2)
23
- @number_of_hmetrics = data.unpack("n").first
6
+ attr_reader :version
7
+ attr_reader :ascent
8
+ attr_reader :descent
9
+ attr_reader :line_gap
10
+ attr_reader :advance_width_max
11
+ attr_reader :min_left_side_bearing
12
+ attr_reader :min_right_side_bearing
13
+ attr_reader :x_max_extent
14
+ attr_reader :carot_slope_rise
15
+ attr_reader :carot_slope_run
16
+ attr_reader :metric_data_format
17
+ attr_reader :number_of_metrics
18
+
19
+ def self.encode(hhea, hmtx)
20
+ raw = hhea.raw
21
+ raw[-2,2] = [hmtx[:number_of_metrics]].pack("n")
22
+ return raw
24
23
  end
24
+
25
+ private
26
+
27
+ def parse!
28
+ @version = read(4, "N").first
29
+ @ascent, @descent, @line_gap = read_signed(3)
30
+ @advance_width_max = read(2, "n").first
31
+
32
+ @min_left_side_bearing, @min_right_side_bearing, @x_max_extent,
33
+ @carot_slope_rise, @carot_slope_run, @caret_offset,
34
+ reserved, reserved, reserved, reserved,
35
+ @metric_data_format = read_signed(11)
36
+
37
+ @number_of_metrics = read(2, "n").first
38
+ end
25
39
  end
26
40
  end
27
- end
41
+ end
@@ -1,20 +1,47 @@
1
+ require 'ttfunk/table'
2
+
1
3
  module TTFunk
2
4
  class Table
3
5
  class Hmtx < Table
4
- def initialize(fh, font, info)
5
- fh.pos = info[:offset]
6
- @values = []
7
-
8
- font.hhea.number_of_hmetrics.times do
9
- advance = fh.read(2).unpack("n").first
10
- lsb = to_signed(fh.read(2).unpack("n").first)
11
- @values << [advance,lsb]
6
+ attr_reader :metrics
7
+ attr_reader :left_side_bearings
8
+ attr_reader :widths
9
+
10
+ def self.encode(hmtx, mapping)
11
+ metrics = mapping.keys.sort.map do |new_id|
12
+ metric = hmtx.for(mapping[new_id])
13
+ [metric.advance_width, metric.left_side_bearing]
12
14
  end
13
-
14
- lsb_count = font.hhea.number_of_hmetrics - font.maxp.num_glyphs
15
- pattern = "n#{lsb_count}"
16
- @lsb = fh.read(2*lsb_count).unpack(pattern).map { |e| to_signed(e) }
15
+
16
+ { :number_of_metrics => metrics.length,
17
+ :table => metrics.flatten.pack("n*") }
18
+ end
19
+
20
+ HorizontalMetric = Struct.new(:advance_width, :left_side_bearing)
21
+
22
+ def for(glyph_id)
23
+ @metrics[glyph_id] ||
24
+ HorizontalMetric.new(@metrics.last.advance_width,
25
+ @left_side_bearings[glyph_id - @metrics.length])
17
26
  end
27
+
28
+ private
29
+
30
+ def parse!
31
+ @metrics = []
32
+
33
+ file.horizontal_header.number_of_metrics.times do
34
+ advance = read(2, "n").first
35
+ lsb = read_signed(1).first
36
+ @metrics.push HorizontalMetric.new(advance, lsb)
37
+ end
38
+
39
+ lsb_count = file.maximum_profile.num_glyphs - file.horizontal_header.number_of_metrics
40
+ @left_side_bearings = read_signed(lsb_count)
41
+
42
+ @widths = @metrics.map { |metric| metric.advance_width }
43
+ @widths += [@widths.last] * @left_side_bearings.length
44
+ end
18
45
  end
19
46
  end
20
- end
47
+ end
@@ -1,48 +1,79 @@
1
+ require 'ttfunk/table'
2
+
1
3
  module TTFunk
2
4
  class Table
3
5
  class Kern < Table
4
- def initialize(fh, font, info)
5
- fh.pos = info[:offset]
6
- data = fh.read(4)
7
- @fh = fh
8
- @version, @table_count = data.unpack("n2")
9
-
10
- @table_headers = {}
11
-
12
- @table_count.times do
13
- version, length, coverage = fh.read(6).unpack("n3")
14
- @table_headers[version] = { :length => length,
15
- :coverage => coverage,
16
- :format => coverage >> 8 }
17
- end
18
-
19
- generate_subtables
6
+ attr_reader :version
7
+ attr_reader :tables
8
+
9
+ def self.encode(kerning, mapping)
10
+ return nil unless kerning.exists? && kerning.tables.any?
11
+ tables = kerning.tables.map { |table| table.recode(mapping) }.compact
12
+ return nil if tables.empty?
13
+
14
+ [0, tables.length, tables.join].pack("nnA*")
20
15
  end
21
-
22
- def generate_subtables
23
- @sub_tables = {}
24
- @table_headers.each do |version, data|
25
- if data[:format].zero?
26
- @sub_tables[0] = parse_subtable_format0
16
+
17
+ private
18
+
19
+ def parse!
20
+ @version, num_tables = read(4, "n*")
21
+ @tables = []
22
+
23
+ if @version == 1 # Mac OS X fonts
24
+ @version = (@version << 16) + num_tables
25
+ num_tables = read(4, "N").first
26
+ parse_version_1_tables(num_tables)
27
27
  else
28
- warn "TTFunk does not support kerning tables of format #{data[:format]}"
28
+ parse_version_0_tables(num_tables)
29
29
  end
30
30
  end
31
- end
32
-
33
- def parse_subtable_format0
34
- sub_table = {}
35
- pair_count, search_range, entry_selector, range_shift = @fh.read(8).unpack("n4")
36
-
37
- pair_count.times do
38
- left, right = @fh.read(4).unpack("n2")
39
- fword = to_signed(@fh.read(2).unpack("n").first)
40
- sub_table[[left,right]] = fword
31
+
32
+ def parse_version_0_tables(num_tables)
33
+ # It looks like some MS fonts report their kerning subtable lengths
34
+ # wrong. In one case, the length was reported to be some 19366, and yet
35
+ # the table also claimed to hold 14148 pairs (each pair consisting of 6 bytes).
36
+ # You do the math!
37
+ #
38
+ # We're going to assume that the microsoft fonts hold only a single kerning
39
+ # subtable, which occupies the entire length of the kerning table. Worst
40
+ # case, we lose any other subtables that the font contains, but it's better
41
+ # than reading a truncated kerning table.
42
+ #
43
+ # And what's more, it appears to work. So.
44
+ version, length, coverage = read(6, "n*")
45
+ format = coverage >> 8
46
+
47
+ add_table format, :version => version, :length => length,
48
+ :coverage => coverage, :data => raw[10..-1],
49
+ :vertical => (coverage & 0x1 == 0),
50
+ :minimum => (coverage & 0x2 != 0),
51
+ :cross => (coverage & 0x4 != 0),
52
+ :override => (coverage & 0x8 != 0)
53
+ end
54
+
55
+ def parse_version_1_tables(num_tables)
56
+ num_tables.times do
57
+ length, coverage, tuple_index = read(8, "Nnn")
58
+ format = coverage & 0x0FF
59
+
60
+ add_table format, :length => length, :coverage => coverage,
61
+ :tuple_index => tuple_index, :data => io.read(length-8),
62
+ :vertical => (coverage & 0x8000 != 0),
63
+ :cross => (coverage & 0x4000 != 0),
64
+ :variation => (coverage & 0x2000 != 0)
65
+ end
66
+ end
67
+
68
+ def add_table(format, attributes={})
69
+ if format == 0
70
+ @tables << Kern::Format0.new(attributes)
71
+ else
72
+ # silently ignore unsupported kerning tables
73
+ end
41
74
  end
42
-
43
- return sub_table
44
- end
45
-
46
75
  end
47
76
  end
48
- end
77
+ end
78
+
79
+ require 'ttfunk/table/kern/format0'
@@ -0,0 +1,62 @@
1
+ require 'ttfunk/reader'
2
+
3
+ module TTFunk
4
+ class Table
5
+ class Kern
6
+ class Format0
7
+ include Reader
8
+
9
+ attr_reader :attributes
10
+ attr_reader :pairs
11
+
12
+ def initialize(attributes={})
13
+ @attributes = attributes
14
+
15
+ num_pairs, search_range, entry_selector, range_shift, *pairs =
16
+ attributes.delete(:data).unpack("n*")
17
+
18
+ @pairs = {}
19
+ num_pairs.times do |i|
20
+ break if i*3+2 > pairs.length # sanity check, in case there's a bad length somewhere
21
+ left = pairs[i*3]
22
+ right = pairs[i*3+1]
23
+ value = to_signed(pairs[i*3+2])
24
+ @pairs[[left, right]] = value
25
+ end
26
+ end
27
+
28
+ def vertical?
29
+ @attributes[:vertical]
30
+ end
31
+
32
+ def horizontal?
33
+ !vertical?
34
+ end
35
+
36
+ def cross_stream?
37
+ @attributes[:cross]
38
+ end
39
+
40
+ def recode(mapping)
41
+ subset = []
42
+ pairs.each do |(left, right), value|
43
+ if mapping[left] && mapping[right]
44
+ subset << [mapping[left], mapping[right], value]
45
+ end
46
+ end
47
+
48
+ return nil if subset.empty?
49
+
50
+ num_pairs = subset.length
51
+ search_range = 2 * 2 ** (Math.log(num_pairs) / Math.log(2)).to_i
52
+ entry_selector = (Math.log(search_range / 2) / Math.log(2)).to_i
53
+ range_shift = (2 * num_pairs) - search_range
54
+
55
+ [attributes[:version], num_pairs * 6 + 14, attributes[:coverage],
56
+ num_pairs, search_range, entry_selector, range_shift, subset].
57
+ flatten.pack("n*")
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end