pdf-reader 1.2.0 → 1.3.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.
- data/CHANGELOG +7 -1
- data/README.rdoc +1 -0
- data/Rakefile +23 -8
- data/lib/pdf-reader.rb +3 -1
- data/lib/pdf/hash.rb +5 -1
- data/lib/pdf/reader.rb +8 -1
- data/lib/pdf/reader/afm/Courier-Bold.afm +342 -0
- data/lib/pdf/reader/afm/Courier-BoldOblique.afm +342 -0
- data/lib/pdf/reader/afm/Courier-Oblique.afm +342 -0
- data/lib/pdf/reader/afm/Courier.afm +342 -0
- data/lib/pdf/reader/afm/Helvetica-Bold.afm +2827 -0
- data/lib/pdf/reader/afm/Helvetica-BoldOblique.afm +2827 -0
- data/lib/pdf/reader/afm/Helvetica-Oblique.afm +3051 -0
- data/lib/pdf/reader/afm/Helvetica.afm +3051 -0
- data/lib/pdf/reader/afm/Symbol.afm +213 -0
- data/lib/pdf/reader/afm/Times-Bold.afm +2588 -0
- data/lib/pdf/reader/afm/Times-BoldItalic.afm +2384 -0
- data/lib/pdf/reader/afm/Times-Italic.afm +2667 -0
- data/lib/pdf/reader/afm/Times-Roman.afm +2419 -0
- data/lib/pdf/reader/afm/ZapfDingbats.afm +225 -0
- data/lib/pdf/reader/buffer.rb +14 -6
- data/lib/pdf/reader/cid_widths.rb +61 -0
- data/lib/pdf/reader/cmap.rb +8 -2
- data/lib/pdf/reader/encoding.rb +52 -27
- data/lib/pdf/reader/error.rb +16 -1
- data/lib/pdf/reader/filter.rb +2 -0
- data/lib/pdf/reader/filter/ascii85.rb +3 -1
- data/lib/pdf/reader/filter/ascii_hex.rb +3 -1
- data/lib/pdf/reader/filter/depredict.rb +2 -0
- data/lib/pdf/reader/filter/flate.rb +3 -1
- data/lib/pdf/reader/filter/lzw.rb +1 -0
- data/lib/pdf/reader/filter/null.rb +1 -0
- data/lib/pdf/reader/filter/run_length.rb +2 -1
- data/lib/pdf/reader/font.rb +74 -18
- data/lib/pdf/reader/font_descriptor.rb +80 -0
- data/lib/pdf/reader/glyph_hash.rb +6 -0
- data/lib/pdf/reader/lzw.rb +1 -0
- data/lib/pdf/reader/object_cache.rb +1 -1
- data/lib/pdf/reader/object_hash.rb +1 -1
- data/lib/pdf/reader/page_layout.rb +125 -0
- data/lib/pdf/reader/page_state.rb +172 -69
- data/lib/pdf/reader/page_text_receiver.rb +50 -21
- data/lib/pdf/reader/pages_strategy.rb +17 -4
- data/lib/pdf/reader/parser.rb +25 -52
- data/lib/pdf/reader/print_receiver.rb +5 -0
- data/lib/pdf/reader/reference.rb +2 -0
- data/lib/pdf/reader/register_receiver.rb +1 -1
- data/lib/pdf/reader/standard_security_handler.rb +2 -0
- data/lib/pdf/reader/stream.rb +2 -0
- data/lib/pdf/reader/synchronized_cache.rb +32 -0
- data/lib/pdf/reader/text_receiver.rb +5 -4
- data/lib/pdf/reader/text_run.rb +80 -0
- data/lib/pdf/reader/token.rb +2 -0
- data/lib/pdf/reader/transformation_matrix.rb +194 -0
- data/lib/pdf/reader/width_calculator.rb +11 -0
- data/lib/pdf/reader/width_calculator/built_in.rb +50 -0
- data/lib/pdf/reader/width_calculator/composite.rb +27 -0
- data/lib/pdf/reader/width_calculator/true_type.rb +56 -0
- data/lib/pdf/reader/width_calculator/type_one_or_three.rb +32 -0
- data/lib/pdf/reader/width_calculator/type_zero.rb +24 -0
- data/lib/pdf/reader/xref.rb +9 -2
- metadata +119 -13
@@ -1,3 +1,5 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
1
3
|
################################################################################
|
2
4
|
#
|
3
5
|
# Copyright (C) 2006 Peter J Jones (pjones@pmade.com)
|
@@ -56,9 +58,10 @@ class PDF::Reader
|
|
56
58
|
# == Text Callbacks
|
57
59
|
#
|
58
60
|
# All text passed into these callbacks will be encoded as UTF-8. Depending on where (and when) the
|
59
|
-
# PDF was generated, there's a good chance the text is NOT stored as UTF-8 internally so be
|
60
|
-
# when doing a comparison on strings returned from PDF::Reader (when doing unit tests for
|
61
|
-
# string may not be byte-by-byte identical with the string that was originally
|
61
|
+
# PDF was generated, there's a good chance the text is NOT stored as UTF-8 internally so be
|
62
|
+
# careful when doing a comparison on strings returned from PDF::Reader (when doing unit tests for
|
63
|
+
# example). The string may not be byte-by-byte identical with the string that was originally
|
64
|
+
# written to the PDF.
|
62
65
|
#
|
63
66
|
# - end_text_object
|
64
67
|
# - move_to_start_of_next_line
|
@@ -267,6 +270,16 @@ class PDF::Reader
|
|
267
270
|
end
|
268
271
|
private
|
269
272
|
################################################################################
|
273
|
+
def params_to_utf8(params, font)
|
274
|
+
if params.is_a?(String)
|
275
|
+
font.to_utf8(params)
|
276
|
+
elsif params.is_a?(Array)
|
277
|
+
params.map { |i| params_to_utf8(i, font)}
|
278
|
+
else
|
279
|
+
params
|
280
|
+
end
|
281
|
+
end
|
282
|
+
################################################################################
|
270
283
|
# Walk over all pages in the PDF file, calling the appropriate callbacks for each page and all
|
271
284
|
# its content
|
272
285
|
def walk_pages (page)
|
@@ -363,7 +376,7 @@ class PDF::Reader
|
|
363
376
|
if options[:raw_text]
|
364
377
|
callback("#{OPERATORS[token]}_raw".to_sym, params)
|
365
378
|
end
|
366
|
-
params = fonts[current_font]
|
379
|
+
params = params_to_utf8(params, fonts[current_font])
|
367
380
|
elsif token == "ID"
|
368
381
|
# inline image data, first convert the current params into a more familiar hash
|
369
382
|
map = {}
|
data/lib/pdf/reader/parser.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
1
3
|
################################################################################
|
2
4
|
#
|
3
5
|
# Copyright (C) 2006 Peter J Jones (pjones@pmade.com)
|
@@ -74,8 +76,6 @@ class PDF::Reader
|
|
74
76
|
STRATEGIES[token].call(self, token)
|
75
77
|
elsif token.is_a? PDF::Reader::Reference
|
76
78
|
token
|
77
|
-
elsif token.is_a? Token
|
78
|
-
token
|
79
79
|
elsif operators.has_key? token
|
80
80
|
Token.new(token)
|
81
81
|
elsif token.respond_to?(:to_token)
|
@@ -100,6 +100,7 @@ class PDF::Reader
|
|
100
100
|
|
101
101
|
obj = parse_token
|
102
102
|
post_obj = parse_token
|
103
|
+
|
103
104
|
if post_obj == "stream"
|
104
105
|
stream(obj)
|
105
106
|
else
|
@@ -171,58 +172,30 @@ class PDF::Reader
|
|
171
172
|
return "" if str == ")"
|
172
173
|
Error.assert_equal(parse_token, ")")
|
173
174
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
while idx < str.size
|
178
|
-
chr = str[idx,1]
|
179
|
-
jump = 1
|
180
|
-
|
181
|
-
if chr == "\\"
|
182
|
-
jump = 2
|
183
|
-
case str[idx+1, 1]
|
184
|
-
when "" then jump = 1
|
185
|
-
when "n" then chr = "\n"
|
186
|
-
when "r" then chr = "\r"
|
187
|
-
when "t" then chr = "\t"
|
188
|
-
when "b" then chr = "\b"
|
189
|
-
when "f" then chr = "\f"
|
190
|
-
when "(" then chr = "("
|
191
|
-
when ")" then chr = ")"
|
192
|
-
when "\\" then chr = "\\"
|
193
|
-
when "\n" then
|
194
|
-
chr = ""
|
195
|
-
jump = 2
|
196
|
-
else
|
197
|
-
if str[idx+1,3].match(/\d{3}/)
|
198
|
-
jump = 4
|
199
|
-
chr = str[idx+1,3].oct.chr
|
200
|
-
elsif str[idx+1,2].match(/\d{2}/)
|
201
|
-
jump = 3
|
202
|
-
chr = ("0"+str[idx+1,2]).oct.chr
|
203
|
-
elsif str[idx+1,1].match(/\d/)
|
204
|
-
jump = 2
|
205
|
-
chr = ("00"+str[idx+1,1]).oct.chr
|
206
|
-
else
|
207
|
-
jump = 1
|
208
|
-
chr = ""
|
209
|
-
end
|
210
|
-
|
211
|
-
end
|
212
|
-
elsif chr == "\r" && str[idx+1,1] == "\n"
|
213
|
-
chr = "\n"
|
214
|
-
jump = 2
|
215
|
-
elsif chr == "\n" && str[idx+1,1] == "\r"
|
216
|
-
chr = "\n"
|
217
|
-
jump = 2
|
218
|
-
elsif chr == "\r"
|
219
|
-
chr = "\n"
|
220
|
-
end
|
221
|
-
ret << chr
|
222
|
-
idx += jump
|
175
|
+
str.gsub!(/\\([nrtbf()\\\n]|\d{1,3})?|\r\n?|\n\r/m) do |match|
|
176
|
+
MAPPING[match] || ""
|
223
177
|
end
|
224
|
-
|
178
|
+
str
|
225
179
|
end
|
180
|
+
|
181
|
+
MAPPING = {
|
182
|
+
"\r" => "\n",
|
183
|
+
"\n\r" => "\n",
|
184
|
+
"\r\n" => "\n",
|
185
|
+
"\\n" => "\n",
|
186
|
+
"\\r" => "\r",
|
187
|
+
"\\t" => "\t",
|
188
|
+
"\\b" => "\b",
|
189
|
+
"\\f" => "\f",
|
190
|
+
"\\(" => "(",
|
191
|
+
"\\)" => ")",
|
192
|
+
"\\\\" => "\\",
|
193
|
+
"\\\n" => "",
|
194
|
+
}
|
195
|
+
0.upto(9) { |n| MAPPING["\\00"+n.to_s] = ("00"+n.to_s).oct.chr }
|
196
|
+
0.upto(99) { |n| MAPPING["\\0"+n.to_s] = ("0"+n.to_s).oct.chr }
|
197
|
+
0.upto(377) { |n| MAPPING["\\"+n.to_s] = n.to_s.oct.chr }
|
198
|
+
|
226
199
|
################################################################################
|
227
200
|
# Decodes the contents of a PDF Stream and returns it as a Ruby String.
|
228
201
|
def stream (dict)
|
data/lib/pdf/reader/reference.rb
CHANGED
data/lib/pdf/reader/stream.rb
CHANGED
@@ -0,0 +1,32 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# utilities.rb : General-purpose utility classes which don't fit anywhere else
|
4
|
+
#
|
5
|
+
# Copyright August 2012, Alex Dowad. All Rights Reserved.
|
6
|
+
#
|
7
|
+
# This is free software. Please see the LICENSE and COPYING files for details.
|
8
|
+
#
|
9
|
+
# This was originally written for the prawn gem.
|
10
|
+
|
11
|
+
require 'thread'
|
12
|
+
|
13
|
+
class PDF::Reader
|
14
|
+
|
15
|
+
# Throughout the pdf-reader codebase, repeated calculations which can benefit
|
16
|
+
# from caching are made In some cases, caching and reusing results can not
|
17
|
+
# only save CPU cycles but also greatly reduce memory requirements But at the
|
18
|
+
# same time, we don't want to throw away thread safety We have two
|
19
|
+
# interchangeable thread-safe cache implementations:
|
20
|
+
class SynchronizedCache
|
21
|
+
def initialize
|
22
|
+
@cache = {}
|
23
|
+
@mutex = Mutex.new
|
24
|
+
end
|
25
|
+
def [](key)
|
26
|
+
@mutex.synchronize { @cache[key] }
|
27
|
+
end
|
28
|
+
def []=(key,value)
|
29
|
+
@mutex.synchronize { @cache[key] = value }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
1
3
|
################################################################################
|
2
4
|
#
|
3
5
|
# Copyright (C) 2006 Peter J Jones (pjones@pmade.com)
|
@@ -9,10 +11,10 @@
|
|
9
11
|
# distribute, sublicense, and/or sell copies of the Software, and to
|
10
12
|
# permit persons to whom the Software is furnished to do so, subject to
|
11
13
|
# the following conditions:
|
12
|
-
#
|
14
|
+
#
|
13
15
|
# The above copyright notice and this permission notice shall be
|
14
16
|
# included in all copies or substantial portions of the Software.
|
15
|
-
#
|
17
|
+
#
|
16
18
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
19
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
20
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
@@ -161,7 +163,7 @@ class PDF::Reader
|
|
161
163
|
|
162
164
|
x = (@tm[2,0]/TS_UNITS_PER_H_CHAR).to_i
|
163
165
|
y = (ury - (@tm[2,1]/TS_UNITS_PER_V_CHAR)).to_i
|
164
|
-
|
166
|
+
|
165
167
|
#puts "rendering '#{string}' to #{x}x#{y}"
|
166
168
|
|
167
169
|
place = (@output[y] ||= (" " * urx.to_i))
|
@@ -255,7 +257,6 @@ class PDF::Reader
|
|
255
257
|
@smallest_y_loc = key if key < @smallest_y_loc
|
256
258
|
@location = key
|
257
259
|
@line = @displacement[key]
|
258
|
-
#puts "calculate_line_and_location: @location=#@location @line=#@line smallest_y_loc=#@smallest_y_loc"
|
259
260
|
end
|
260
261
|
################################################################################
|
261
262
|
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
class PDF::Reader
|
4
|
+
# A value object that represents one or more consecutive characters on a page.
|
5
|
+
class TextRun
|
6
|
+
include Comparable
|
7
|
+
|
8
|
+
attr_reader :x, :y, :width, :font_size, :text
|
9
|
+
|
10
|
+
alias :to_s :text
|
11
|
+
|
12
|
+
def initialize(x, y, width, font_size, text)
|
13
|
+
@x = x
|
14
|
+
@y = y
|
15
|
+
@width = width
|
16
|
+
@font_size = font_size.floor
|
17
|
+
@text = text
|
18
|
+
end
|
19
|
+
|
20
|
+
# Allows collections of TextRun objects to be sorted. They will be sorted
|
21
|
+
# in order of their position on a cartesian plain - Top Left to Bottom Right
|
22
|
+
def <=>(other)
|
23
|
+
if x == other.x && y == other.y
|
24
|
+
0
|
25
|
+
elsif y < other.y
|
26
|
+
1
|
27
|
+
elsif y > other.y
|
28
|
+
-1
|
29
|
+
elsif x < other.x
|
30
|
+
-1
|
31
|
+
elsif x > other.x
|
32
|
+
1
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def endx
|
37
|
+
@endx ||= x + width
|
38
|
+
end
|
39
|
+
|
40
|
+
def mean_character_width
|
41
|
+
@width / character_count
|
42
|
+
end
|
43
|
+
|
44
|
+
def mergable?(other)
|
45
|
+
y.to_i == other.y.to_i && font_size == other.font_size && mergable_range.include?(other.x)
|
46
|
+
end
|
47
|
+
|
48
|
+
def +(other)
|
49
|
+
raise ArgumentError, "#{other} cannot be merged with this run" unless mergable?(other)
|
50
|
+
|
51
|
+
if (other.x - endx) <( font_size * 0.2)
|
52
|
+
TextRun.new(x, y, other.endx - x, font_size, text + other.text)
|
53
|
+
else
|
54
|
+
TextRun.new(x, y, other.endx - x, font_size, "#{text} #{other.text}")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def inspect
|
59
|
+
"#{text} w:#{width} f:#{font_size} @#{x},#{y}"
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def mergable_range
|
65
|
+
@mergable_range ||= Range.new(endx - 3, endx + font_size)
|
66
|
+
end
|
67
|
+
|
68
|
+
def character_count
|
69
|
+
if @text.size == 1
|
70
|
+
1.0
|
71
|
+
elsif @text.respond_to?(:bytesize)
|
72
|
+
# M17N aware VM
|
73
|
+
# so we can trust String#size to return a character count
|
74
|
+
@text.size.to_f
|
75
|
+
else
|
76
|
+
text.unpack("U*").size.to_f
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/lib/pdf/reader/token.rb
CHANGED
@@ -0,0 +1,194 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
class PDF::Reader
|
4
|
+
# co-ordinate systems in PDF files are specified using a 3x3 matrix that looks
|
5
|
+
# something like this:
|
6
|
+
#
|
7
|
+
# [ a b 0 ]
|
8
|
+
# [ c d 0 ]
|
9
|
+
# [ e f 1 ]
|
10
|
+
#
|
11
|
+
# Because the final column never changes, we can represent each matrix using
|
12
|
+
# only 6 numbers. This is important to save CPU time, memory and GC pressure
|
13
|
+
# caused by allocating too many unnecessary objects.
|
14
|
+
class TransformationMatrix
|
15
|
+
attr_reader :a, :b, :c, :d, :e, :f
|
16
|
+
|
17
|
+
def initialize(a, b, c, d, e, f)
|
18
|
+
@a, @b, @c, @d, @e, @f = a, b, c, d, e, f
|
19
|
+
end
|
20
|
+
|
21
|
+
def inspect
|
22
|
+
"#{a}, #{b}, 0,\n#{c}, #{d}, #{0},\n#{e}, #{f}, 1"
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_a
|
26
|
+
[@a,@b,0,
|
27
|
+
@c,@d,0,
|
28
|
+
@e,@f,1]
|
29
|
+
end
|
30
|
+
|
31
|
+
# multiply this matrix with another.
|
32
|
+
#
|
33
|
+
# the second matrix is represented by the 6 scalar values that are changeable
|
34
|
+
# in a PDF transformation matrix.
|
35
|
+
#
|
36
|
+
# WARNING: This mutates the current matrix to avoid allocating memory when
|
37
|
+
# we don't need too. Matrices are multiplied ALL THE FREAKING TIME
|
38
|
+
# so this is a worthwhile optimisation
|
39
|
+
#
|
40
|
+
# NOTE: When multiplying matrices, ordering matters. Double check
|
41
|
+
# the PDF spec to ensure you're multiplying things correctly.
|
42
|
+
#
|
43
|
+
# NOTE: see Section 8.3.3, PDF 32000-1:2008, pp 119
|
44
|
+
#
|
45
|
+
# NOTE: The if statements in this method are ordered to prefer optimisations
|
46
|
+
# that allocate fewer objects
|
47
|
+
#
|
48
|
+
# TODO: it might be worth adding an optimised path for vertical
|
49
|
+
# displacement to speed up processing documents that use vertical
|
50
|
+
# writing systems
|
51
|
+
#
|
52
|
+
def multiply!(a,b=nil,c=nil, d=nil,e=nil,f=nil)
|
53
|
+
if a == 1 && b == 0 && c == 0 && d == 1 && e == 0 && f == 0
|
54
|
+
# the identity matrix, no effect
|
55
|
+
self
|
56
|
+
elsif @a == 1 && @b == 0 && @c == 0 && @d == 1 && @e == 0 && @f == 0
|
57
|
+
# I'm the identity matrix, so just copy values across
|
58
|
+
@a = a
|
59
|
+
@b = b
|
60
|
+
@c = c
|
61
|
+
@d = d
|
62
|
+
@e = e
|
63
|
+
@f = f
|
64
|
+
elsif a == 1 && b == 0 && c == 0 && d == 1 && f == 0
|
65
|
+
# the other matrix is a horizontal displacement
|
66
|
+
horizontal_displacement_multiply!(e)
|
67
|
+
elsif @a == 1 && @b == 0 && @c == 0 && @d == 1 && @f == 0
|
68
|
+
# I'm a horizontal displacement
|
69
|
+
horizontal_displacement_multiply_reversed!(a,b,c,d,e,f)
|
70
|
+
elsif @a != 1 && @b == 0 && @c == 0 && @d != 1 && @e == 0 && @f == 0
|
71
|
+
# I'm a xy scale
|
72
|
+
xy_scaling_multiply_reversed!(a,b,c,d,e,f)
|
73
|
+
elsif a != 1 && b == 0 && c == 0 && d != 1 && e == 0 && f == 0
|
74
|
+
# the other matrix is an xy scale
|
75
|
+
xy_scaling_multiply!(a,b,c,d,e,f)
|
76
|
+
else
|
77
|
+
faster_multiply!(a,b,c, d,e,f)
|
78
|
+
end
|
79
|
+
self
|
80
|
+
end
|
81
|
+
|
82
|
+
# Optimised method for when the second matrix in the calculation is
|
83
|
+
# a simple horizontal displacement.
|
84
|
+
#
|
85
|
+
# Like this:
|
86
|
+
#
|
87
|
+
# [ 1 2 0 ] [ 1 0 0 ]
|
88
|
+
# [ 3 4 0 ] x [ 0 1 0 ]
|
89
|
+
# [ 5 6 1 ] [ e2 0 1 ]
|
90
|
+
#
|
91
|
+
def horizontal_displacement_multiply!(e2)
|
92
|
+
@e = @e + e2
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
# Optimised method for when the first matrix in the calculation is
|
98
|
+
# a simple horizontal displacement.
|
99
|
+
#
|
100
|
+
# Like this:
|
101
|
+
#
|
102
|
+
# [ 1 0 0 ] [ 1 2 0 ]
|
103
|
+
# [ 0 1 0 ] x [ 3 4 0 ]
|
104
|
+
# [ 5 0 1 ] [ 5 6 1 ]
|
105
|
+
#
|
106
|
+
def horizontal_displacement_multiply_reversed!(a2,b2,c2,d2,e2,f2)
|
107
|
+
newa = a2
|
108
|
+
newb = b2
|
109
|
+
newc = c2
|
110
|
+
newd = d2
|
111
|
+
newe = (@e * a2) + e2
|
112
|
+
newf = (@e * b2) + f2
|
113
|
+
@a, @b, @c, @d, @e, @f = newa, newb, newc, newd, newe, newf
|
114
|
+
end
|
115
|
+
|
116
|
+
# Optimised method for when the second matrix in the calculation is
|
117
|
+
# an X and Y scale
|
118
|
+
#
|
119
|
+
# Like this:
|
120
|
+
#
|
121
|
+
# [ 1 2 0 ] [ 5 0 0 ]
|
122
|
+
# [ 3 4 0 ] x [ 0 5 0 ]
|
123
|
+
# [ 5 6 1 ] [ 0 0 1 ]
|
124
|
+
#
|
125
|
+
def xy_scaling_multiply!(a2,b2,c2,d2,e2,f2)
|
126
|
+
newa = @a * a2
|
127
|
+
newb = @b * d2
|
128
|
+
newc = @c * a2
|
129
|
+
newd = @d * d2
|
130
|
+
newe = @e * a2
|
131
|
+
newf = @f * d2
|
132
|
+
@a, @b, @c, @d, @e, @f = newa, newb, newc, newd, newe, newf
|
133
|
+
end
|
134
|
+
|
135
|
+
# Optimised method for when the first matrix in the calculation is
|
136
|
+
# an X and Y scale
|
137
|
+
#
|
138
|
+
# Like this:
|
139
|
+
#
|
140
|
+
# [ 5 0 0 ] [ 1 2 0 ]
|
141
|
+
# [ 0 5 0 ] x [ 3 4 0 ]
|
142
|
+
# [ 0 0 1 ] [ 5 6 1 ]
|
143
|
+
#
|
144
|
+
def xy_scaling_multiply_reversed!(a2,b2,c2,d2,e2,f2)
|
145
|
+
newa = @a * a2
|
146
|
+
newb = @a * b2
|
147
|
+
newc = @d * c2
|
148
|
+
newd = @d * d2
|
149
|
+
newe = e2
|
150
|
+
newf = f2
|
151
|
+
@a, @b, @c, @d, @e, @f = newa, newb, newc, newd, newe, newf
|
152
|
+
end
|
153
|
+
|
154
|
+
# A general solution to multiplying two 3x3 matrixes. This is correct in all cases,
|
155
|
+
# but slower due to excessive object allocations. It's not actually used in any
|
156
|
+
# active code paths, but is here for reference. Use faster_multiply instead.
|
157
|
+
#
|
158
|
+
# Like this:
|
159
|
+
#
|
160
|
+
# [ a b 0 ] [ a b 0 ]
|
161
|
+
# [ c d 0 ] x [ c d 0 ]
|
162
|
+
# [ e f 1 ] [ e f 1 ]
|
163
|
+
#
|
164
|
+
def regular_multiply!(a2,b2,c2,d2,e2,f2)
|
165
|
+
newa = (@a * a2) + (@b * c2) + (0 * e2)
|
166
|
+
newb = (@a * b2) + (@b * d2) + (0 * f2)
|
167
|
+
newc = (@c * a2) + (@d * c2) + (0 * e2)
|
168
|
+
newd = (@c * b2) + (@d * d2) + (0 * f2)
|
169
|
+
newe = (@e * a2) + (@f * c2) + (1 * e2)
|
170
|
+
newf = (@e * b2) + (@f * d2) + (1 * f2)
|
171
|
+
@a, @b, @c, @d, @e, @f = newa, newb, newc, newd, newe, newf
|
172
|
+
end
|
173
|
+
|
174
|
+
# A general solution for multiplying two matrices when we know all values
|
175
|
+
# in the final column are fixed. This is the fallback method for when none
|
176
|
+
# of the optimised methods are applicable.
|
177
|
+
#
|
178
|
+
# Like this:
|
179
|
+
#
|
180
|
+
# [ a b 0 ] [ a b 0 ]
|
181
|
+
# [ c d 0 ] x [ c d 0 ]
|
182
|
+
# [ e f 1 ] [ e f 1 ]
|
183
|
+
#
|
184
|
+
def faster_multiply!(a2,b2,c2, d2,e2,f2)
|
185
|
+
newa = (@a * a2) + (@b * c2)
|
186
|
+
newb = (@a * b2) + (@b * d2)
|
187
|
+
newc = (@c * a2) + (@d * c2)
|
188
|
+
newd = (@c * b2) + (@d * d2)
|
189
|
+
newe = (@e * a2) + (@f * c2) + e2
|
190
|
+
newf = (@e * b2) + (@f * d2) + f2
|
191
|
+
@a, @b, @c, @d, @e, @f = newa, newb, newc, newd, newe, newf
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|