preflight 0.0.4 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,11 @@
1
+ v0.0.6 (25th April 2011)
2
+ - more improvements to the MinPpi rule. Improves accuracy
3
+ in some situtations
4
+
5
+ v0.0.5 (25th April 2011)
6
+ - bug: improve accuracy of MinPpi rule, it was incorrectly calculating the
7
+ ppi of many images
8
+
1
9
  v0.0.4 (24th April 2011)
2
10
  - bug: fix occasional crash due to not dereferencing an object
3
11
  - bug: improve accuracy of rules that check box-related page attributes
@@ -7,5 +7,5 @@ require 'preflight/rules'
7
7
  require 'preflight/profiles'
8
8
 
9
9
  module Preflight
10
- VERSION = "0.0.4"
10
+ VERSION = "0.0.6"
11
11
  end
@@ -1,5 +1,8 @@
1
1
  # coding: utf-8
2
2
 
3
+ require 'yaml'
4
+ require 'matrix'
5
+
3
6
  module Preflight
4
7
  module Rules
5
8
 
@@ -9,15 +12,30 @@ module Preflight
9
12
  class MinPpi
10
13
  include Preflight::Measurements
11
14
 
15
+ DEFAULT_GRAPHICS_STATE = {
16
+ :ctm => Matrix.identity(3)
17
+ }
18
+
12
19
  attr_reader :messages
13
20
 
14
21
  def initialize(min_ppi)
15
22
  @min_ppi = min_ppi.to_i
16
23
  @messages = []
17
- @last_matrix = []
18
24
  @page_num = 0
19
25
  end
20
26
 
27
+ def save_graphics_state
28
+ @stack.push clone_state
29
+ end
30
+
31
+ def restore_graphics_state
32
+ @stack.pop
33
+ end
34
+
35
+ def state
36
+ @stack.last
37
+ end
38
+
21
39
  # store sample width and height for each image on the current page
22
40
  #
23
41
  def resource_xobject(label, stream)
@@ -29,12 +47,24 @@ module Preflight
29
47
  ]
30
48
  end
31
49
 
32
- # track the most recent matrix transform.
50
+ # update the current transformation matrix.
33
51
  #
34
- # TODO: This needs to be smarter at tracking the graphics state stack
52
+ # If the CTM is currently undefined, just store the new values.
53
+ #
54
+ # If there's an existing CTM, then multiple the existing matrix
55
+ # with the new matrix to form the updated matrix.
35
56
  #
36
57
  def concatenate_matrix(*args)
37
- @last_matrix = args
58
+ transform = Matrix[
59
+ [args[0], args[1], 0],
60
+ [args[2], args[3], 0],
61
+ [args[4], args[5], 1]
62
+ ]
63
+ if state[:ctm]
64
+ state[:ctm] = transform * state[:ctm]
65
+ else
66
+ state[:ctm] = transform
67
+ end
38
68
  end
39
69
 
40
70
  # As each image is drawn on the canvas, determine the amount of device
@@ -44,8 +74,8 @@ module Preflight
44
74
  return unless @images[label]
45
75
 
46
76
  sample_w, sample_h = *@images[label]
47
- device_w = pt2in(@last_matrix[0])
48
- device_h = pt2in(@last_matrix[3])
77
+ device_w = pt2in(image_width)
78
+ device_h = pt2in(image_height)
49
79
 
50
80
  horizontal_ppi = (sample_w / device_w).round(3)
51
81
  vertical_ppi = (sample_h / device_h).round(3)
@@ -60,7 +90,81 @@ module Preflight
60
90
  def begin_page(hash = {})
61
91
  @images = {}
62
92
  @page_num += 1
93
+ @stack = [DEFAULT_GRAPHICS_STATE]
94
+ end
95
+
96
+ private
97
+
98
+ # return the current transformation matrix
99
+ #
100
+ def ctm
101
+ state[:ctm]
102
+ end
103
+
104
+ # transform x and y co-ordinates from the current user space to the
105
+ # underlying device space.
106
+ #
107
+ def transform(point, z = 1)
108
+ Point.new(
109
+ (ctm[0,0] * point.x) + (ctm[1,0] * point.y) + (ctm[2,0] * z),
110
+ (ctm[0,1] * point.x) + (ctm[1,1] * point.y) + (ctm[2,1] * z)
111
+ )
112
+ end
113
+
114
+ # return a height of an image in the current device space. Auto
115
+ # handles the translation from image space to device space.
116
+ #
117
+ def image_height
118
+ bottom_left = transform(Point.new(0, 0))
119
+ top_left = transform(Point.new(0, 1))
120
+
121
+ bottom_left.distance(top_left)
122
+ end
123
+
124
+ # return a width of an image in the current device space. Auto
125
+ # handles the translation from image space to device space.
126
+ #
127
+ def image_width
128
+ bottom_left = transform(Point.new(0, 0))
129
+ bottom_right = transform(Point.new(1, 0))
130
+
131
+ bottom_left.distance(bottom_right)
132
+ end
133
+
134
+ # when save_graphics_state is called, we need to push a new copy of the
135
+ # current state onto the stack. That way any modifications to the state
136
+ # will be undone once restore_graphics_state is called.
137
+ #
138
+ # This returns a deep clone of the current state, ensuring changes are
139
+ # keep separate from earlier states.
140
+ #
141
+ # YAML is used to round-trip the state through a string to easily perform
142
+ # the deep clone. Kinda hacky, but effective.
143
+ #
144
+ def clone_state
145
+ if @stack.empty?
146
+ {}
147
+ else
148
+ yaml_state = YAML.dump(@stack.last)
149
+ YAML.load(yaml_state)
150
+ end
151
+ end
152
+
153
+ # private class for representing points on a cartesian plain. Used
154
+ # to simplify maths in the MinPpi class.
155
+ #
156
+ class Point
157
+ attr_reader :x, :y
158
+
159
+ def initialize(x,y)
160
+ @x, @y = x,y
161
+ end
162
+
163
+ def distance(point)
164
+ Math.hypot(point.x - x, point.y - y)
165
+ end
63
166
  end
167
+
64
168
  end
65
169
  end
66
170
  end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 4
9
- version: 0.0.4
8
+ - 6
9
+ version: 0.0.6
10
10
  platform: ruby
11
11
  authors:
12
12
  - James Healy
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2011-04-24 00:00:00 +10:00
17
+ date: 2011-04-26 00:00:00 +10:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency