preflight 0.0.4 → 0.0.6

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 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