class PDF::Reader::TransformationMatrix

co-ordinate systems in PDF files are specified using a 3x3 matrix that looks something like this:

[ a b 0 ]
[ c d 0 ]
[ e f 1 ]

Because the final column never changes, we can represent each matrix using only 6 numbers. This is important to save CPU time, memory and GC pressure caused by allocating too many unnecessary objects.

Attributes

a[R]

: Numeric

b[R]

: Numeric

c[R]

: Numeric

d[R]

: Numeric

e[R]

: Numeric

f[R]

: Numeric

Public Class Methods

new(a, b, c, d, e, f) click to toggle source

: (Numeric, Numeric, Numeric, Numeric, Numeric, Numeric) -> void

# File lib/pdf/reader/transformation_matrix.rb, line 36
def initialize(a, b, c, d, e, f)
  @a = a
  @b = b
  @c = c
  @d = d
  @e = e
  @f = f
end

Public Instance Methods

horizontal_displacement_multiply!(e2) click to toggle source
Optimised method for when the second matrix in the calculation is
a simple horizontal displacement.

Like this:

  [ 1 2 0 ]   [ 1  0 0 ]
  [ 3 4 0 ] x [ 0  1 0 ]
  [ 5 6 1 ]   [ e2 0 1 ]

: (Numeric) -> void

# File lib/pdf/reader/transformation_matrix.rb, line 119
def horizontal_displacement_multiply!(e2)
  @e = @e + e2
end
inspect() click to toggle source

: () -> String

# File lib/pdf/reader/transformation_matrix.rb, line 46
def inspect
  "#{a}, #{b}, 0,\n#{c}, #{d}, #{0},\n#{e}, #{f}, 1"
end
multiply!(a,b,c, d,e,f) click to toggle source
multiply this matrix with another.

the second matrix is represented by the 6 scalar values that are changeable
in a PDF transformation matrix.

WARNING: This mutates the current matrix to avoid allocating memory when
         we don't need too. Matrices are multiplied ALL THE FREAKING TIME
         so this is a worthwhile optimisation

NOTE: When multiplying matrices, ordering matters. Double check
      the PDF spec to ensure you're multiplying things correctly.

NOTE: see Section 8.3.3, PDF 32000-1:2008, pp 119

NOTE: The if statements in this method are ordered to prefer optimisations
      that allocate fewer objects

TODO: it might be worth adding an optimised path for vertical
      displacement to speed up processing documents that use vertical
      writing systems

: (Numeric, Numeric, Numeric, Numeric, Numeric, Numeric) -> PDF::Reader::TransformationMatrix

# File lib/pdf/reader/transformation_matrix.rb, line 79
def multiply!(a,b,c, d,e,f)
  if a == 1 && b == 0 && c == 0 && d == 1 && e == 0 && f == 0
    # the identity matrix, no effect
    self
  elsif @a == 1 && @b == 0 && @c == 0 && @d == 1 && @e == 0 && @f == 0
    # I'm the identity matrix, so just copy values across
    @a = a
    @b = b
    @c = c
    @d = d
    @e = e
    @f = f
  elsif a == 1 && b == 0 && c == 0 && d == 1 && f == 0
    # the other matrix is a horizontal displacement
    horizontal_displacement_multiply!(e)
  elsif @a == 1 && @b == 0 && @c == 0 && @d == 1 && @f == 0
    # I'm a horizontal displacement
    horizontal_displacement_multiply_reversed!(a,b,c,d,e,f)
  elsif @a != 1 && @b == 0 && @c == 0 && @d != 1 && @e == 0 && @f == 0
    # I'm a xy scale
    xy_scaling_multiply_reversed!(a,b,c,d,e,f)
  elsif a != 1 && b == 0 && c == 0 && d != 1 && e == 0 && f == 0
    # the other matrix is an xy scale
    xy_scaling_multiply!(a,b,c,d,e,f)
  else
    faster_multiply!(a,b,c, d,e,f)
  end
  self
end
to_a() click to toggle source

: () -> [Numeric]

# File lib/pdf/reader/transformation_matrix.rb, line 51
def to_a
  [@a,@b,0,
   @c,@d,0,
   @e,@f,1]
end

Private Instance Methods

faster_multiply!(a2,b2,c2, d2,e2,f2) click to toggle source
A general solution for multiplying two matrices when we know all values
in the final column are fixed. This is the fallback method for when none
of the optimised methods are applicable.

Like this:

  [ a b 0 ]   [ a b 0 ]
  [ c d 0 ] x [ c d 0 ]
  [ e f 1 ]   [ e f 1 ]

: (Numeric, Numeric, Numeric, Numeric, Numeric, Numeric) -> void

# File lib/pdf/reader/transformation_matrix.rb, line 217
def faster_multiply!(a2,b2,c2, d2,e2,f2)
  newa = (@a * a2) + (@b * c2)
  newb = (@a * b2) + (@b * d2)
  newc = (@c * a2) + (@d * c2)
  newd = (@c * b2) + (@d * d2)
  newe = (@e * a2) + (@f * c2) + e2
  newf = (@e * b2) + (@f * d2) + f2
  @a, @b, @c, @d, @e, @f = newa, newb, newc, newd, newe, newf
end
horizontal_displacement_multiply_reversed!(a2,b2,c2,d2,e2,f2) click to toggle source
Optimised method for when the first matrix in the calculation is
a simple horizontal displacement.

Like this:

  [ 1 0 0 ]   [ 1 2 0 ]
  [ 0 1 0 ] x [ 3 4 0 ]
  [ 5 0 1 ]   [ 5 6 1 ]

: (Numeric, Numeric, Numeric, Numeric, Numeric, Numeric) -> void

# File lib/pdf/reader/transformation_matrix.rb, line 135
def horizontal_displacement_multiply_reversed!(a2,b2,c2,d2,e2,f2)
  newa = a2
  newb = b2
  newc = c2
  newd = d2
  newe = (@e * a2) + e2
  newf = (@e * b2) + f2
  @a, @b, @c, @d, @e, @f = newa, newb, newc, newd, newe, newf
end
regular_multiply!(a2,b2,c2,d2,e2,f2) click to toggle source
A general solution to multiplying two 3x3 matrixes. This is correct in all cases,
but slower due to excessive object allocations. It's not actually used in any
active code paths, but is here for reference. Use faster_multiply instead.

Like this:

  [ a b 0 ]   [ a b 0 ]
  [ c d 0 ] x [ c d 0 ]
  [ e f 1 ]   [ e f 1 ]

: (Numeric, Numeric, Numeric, Numeric, Numeric, Numeric) -> void

# File lib/pdf/reader/transformation_matrix.rb, line 196
def regular_multiply!(a2,b2,c2,d2,e2,f2)
  newa = (@a * a2) + (@b * c2) + (e2 * 0)
  newb = (@a * b2) + (@b * d2) + (f2 * 0)
  newc = (@c * a2) + (@d * c2) + (e2 * 0)
  newd = (@c * b2) + (@d * d2) + (f2 * 0)
  newe = (@e * a2) + (@f * c2) + (e2 * 1)
  newf = (@e * b2) + (@f * d2) + (f2 * 1)
  @a, @b, @c, @d, @e, @f = newa, newb, newc, newd, newe, newf
end
xy_scaling_multiply!(a2,b2,c2,d2,e2,f2) click to toggle source
Optimised method for when the second matrix in the calculation is
an X and Y scale

Like this:

  [ 1 2 0 ]   [ 5 0 0 ]
  [ 3 4 0 ] x [ 0 5 0 ]
  [ 5 6 1 ]   [ 0 0 1 ]

: (Numeric, Numeric, Numeric, Numeric, Numeric, Numeric) -> void

# File lib/pdf/reader/transformation_matrix.rb, line 155
def xy_scaling_multiply!(a2,b2,c2,d2,e2,f2)
  newa = @a * a2
  newb = @b * d2
  newc = @c * a2
  newd = @d * d2
  newe = @e * a2
  newf = @f * d2
  @a, @b, @c, @d, @e, @f = newa, newb, newc, newd, newe, newf
end
xy_scaling_multiply_reversed!(a2,b2,c2,d2,e2,f2) click to toggle source
Optimised method for when the first matrix in the calculation is
an X and Y scale

Like this:

  [ 5 0 0 ]   [ 1 2 0 ]
  [ 0 5 0 ] x [ 3 4 0 ]
  [ 0 0 1 ]   [ 5 6 1 ]

: (Numeric, Numeric, Numeric, Numeric, Numeric, Numeric) -> void

# File lib/pdf/reader/transformation_matrix.rb, line 175
def xy_scaling_multiply_reversed!(a2,b2,c2,d2,e2,f2)
  newa = @a * a2
  newb = @a * b2
  newc = @d * c2
  newd = @d * d2
  newe = e2
  newf = f2
  @a, @b, @c, @d, @e, @f = newa, newb, newc, newd, newe, newf
end