preflight 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +8 -1
- data/README.rdoc +21 -4
- data/lib/preflight.rb +1 -0
- data/lib/preflight/issue.rb +35 -0
- data/lib/preflight/profile.rb +38 -16
- data/lib/preflight/profiles/pdfa1a.rb +0 -1
- data/lib/preflight/profiles/pdfx1a.rb +4 -3
- data/lib/preflight/rules.rb +17 -2
- data/lib/preflight/rules/box_nesting.rb +35 -23
- data/lib/preflight/rules/compression_algorithms.rb +14 -4
- data/lib/preflight/rules/consistent_boxes.rb +63 -0
- data/lib/preflight/rules/cropbox_matches_mediabox.rb +38 -0
- data/lib/preflight/rules/document_id.rb +14 -2
- data/lib/preflight/rules/info_has_keys.rb +15 -3
- data/lib/preflight/rules/info_specifies_trapping.rb +16 -3
- data/lib/preflight/rules/match_info_entries.rb +17 -3
- data/lib/preflight/rules/max_ink_density.rb +69 -0
- data/lib/preflight/rules/max_version.rb +14 -2
- data/lib/preflight/rules/mediabox_at_origin.rb +42 -0
- data/lib/preflight/rules/min_bleed.rb +171 -0
- data/lib/preflight/rules/min_ppi.rb +54 -116
- data/lib/preflight/rules/no_cmyk.rb +113 -0
- data/lib/preflight/rules/no_filespecs.rb +15 -5
- data/lib/preflight/rules/no_font_subsets.rb +15 -6
- data/lib/preflight/rules/no_gray.rb +105 -0
- data/lib/preflight/rules/no_page_rotation.rb +36 -0
- data/lib/preflight/rules/no_private_data.rb +37 -0
- data/lib/preflight/rules/no_registration_black.rb +102 -0
- data/lib/preflight/rules/no_rgb.rb +112 -0
- data/lib/preflight/rules/no_separation.rb +85 -0
- data/lib/preflight/rules/no_transparency.rb +90 -0
- data/lib/preflight/rules/only_embedded_fonts.rb +28 -14
- data/lib/preflight/rules/output_intent_for_pdfx.rb +14 -2
- data/lib/preflight/rules/page_box_height.rb +88 -0
- data/lib/preflight/rules/page_box_size.rb +106 -0
- data/lib/preflight/rules/page_box_width.rb +88 -0
- data/lib/preflight/rules/page_count.rb +87 -0
- data/lib/preflight/rules/pdfx_output_intent_has_keys.rb +12 -2
- data/lib/preflight/rules/print_boxes.rb +21 -19
- data/lib/preflight/rules/root_has_keys.rb +15 -3
- metadata +97 -113
- data/lib/preflight/rules/no_encryption.rb +0 -16
- data/lib/preflight/rules/no_proprietary_fonts.rb +0 -50
@@ -0,0 +1,113 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
module Preflight
|
6
|
+
module Rules
|
7
|
+
|
8
|
+
# Some print workflows forbid the use of CMYK colour.
|
9
|
+
#
|
10
|
+
# Arguments: none
|
11
|
+
#
|
12
|
+
# Usage:
|
13
|
+
#
|
14
|
+
# class MyPreflight
|
15
|
+
# include Preflight::Profile
|
16
|
+
#
|
17
|
+
# rule Preflight::Rules::NoCmyk
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
class NoCmyk
|
21
|
+
extend Forwardable
|
22
|
+
|
23
|
+
# Graphics State Operators
|
24
|
+
def_delegators :@state, :save_graphics_state, :restore_graphics_state
|
25
|
+
|
26
|
+
# Matrix Operators
|
27
|
+
def_delegators :@state, :concatenate_matrix
|
28
|
+
|
29
|
+
attr_reader :issues
|
30
|
+
|
31
|
+
# we're about to start a new page, reset state
|
32
|
+
#
|
33
|
+
def page=(page)
|
34
|
+
@page = page
|
35
|
+
@state = PDF::Reader::PageState.new(page)
|
36
|
+
@issues = []
|
37
|
+
@resource_labels_seen = []
|
38
|
+
end
|
39
|
+
|
40
|
+
# descend into nested form xobjects
|
41
|
+
#
|
42
|
+
def invoke_xobject(label)
|
43
|
+
@state.invoke_xobject(label) do |xobj|
|
44
|
+
case xobj
|
45
|
+
when PDF::Reader::FormXObject then
|
46
|
+
xobj.walk(self)
|
47
|
+
when PDF::Reader::Stream then
|
48
|
+
check_xobject(xobj)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def set_cmyk_color_for_stroking(c, m, y, k)
|
54
|
+
cmyk_detected(c, m, y, k)
|
55
|
+
end
|
56
|
+
|
57
|
+
def set_cmyk_color_for_nonstroking(c, m, y, k)
|
58
|
+
cmyk_detected(c, m, y, k)
|
59
|
+
end
|
60
|
+
|
61
|
+
def set_stroke_color_space(label)
|
62
|
+
check_color_space(label)
|
63
|
+
end
|
64
|
+
|
65
|
+
def set_nonstroke_color_space(label)
|
66
|
+
check_color_space(label)
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def color_space_is_cmyk?(cs)
|
72
|
+
case cs
|
73
|
+
when Symbol then cs == :DeviceCMYK
|
74
|
+
when Array then
|
75
|
+
cs[0] == :DeviceCMYK || cs[2] == :DeviceCMYK
|
76
|
+
else
|
77
|
+
false
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def check_color_space(label)
|
82
|
+
return if @resource_labels_seen.include?(label)
|
83
|
+
|
84
|
+
if color_space_is_cmyk?(@state.find_color_space(label))
|
85
|
+
@issues << Issue.new("CMYK color detected", self, :page => @page.number)
|
86
|
+
end
|
87
|
+
|
88
|
+
@resource_labels_seen << label
|
89
|
+
end
|
90
|
+
|
91
|
+
def check_xobject(xobject)
|
92
|
+
cs = xobject.hash[:ColorSpace]
|
93
|
+
if cs == :DeviceCMYK
|
94
|
+
@issues << Issue.new("CMYK image detected", self, :page => @page.number)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def cmyk_detected(c, m, y, k)
|
99
|
+
@issues << Issue.new("CMYK color detected", self, :page => @page.number,
|
100
|
+
:cyan => c,
|
101
|
+
:magenta => m,
|
102
|
+
:yellow => y,
|
103
|
+
:k => k)
|
104
|
+
end
|
105
|
+
|
106
|
+
def deref(obj)
|
107
|
+
@page.objects.deref(obj)
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
end
|
@@ -3,14 +3,24 @@
|
|
3
3
|
module Preflight
|
4
4
|
module Rules
|
5
5
|
|
6
|
-
#
|
7
|
-
#
|
6
|
+
# Check the target PDF doesn't use Filespecs to refer to external files.
|
7
|
+
#
|
8
|
+
# Arguments: none
|
9
|
+
#
|
10
|
+
# Usage:
|
11
|
+
#
|
12
|
+
# class MyPreflight
|
13
|
+
# include Preflight::Profile
|
14
|
+
#
|
15
|
+
# rule Preflight::Rules::NoFilespecs
|
16
|
+
# end
|
8
17
|
#
|
9
18
|
class NoFilespecs
|
10
19
|
|
11
|
-
def
|
12
|
-
|
13
|
-
|
20
|
+
def check_hash(ohash)
|
21
|
+
filespecs_count = count_filespec_dicts(ohash)
|
22
|
+
if filespecs_count > 0
|
23
|
+
[Issue.new("File uses at least 1 Filespec to refer to an external file", self, :filespecs_count => filespecs_count)]
|
14
24
|
else
|
15
25
|
[]
|
16
26
|
end
|
@@ -3,18 +3,27 @@
|
|
3
3
|
module Preflight
|
4
4
|
module Rules
|
5
5
|
|
6
|
-
# check a file has no font subsets. Subsets are handy and valid
|
7
|
-
#
|
8
|
-
#
|
6
|
+
# check a file has no font subsets. Subsets are handy and valid in
|
7
|
+
# standards like PDFX/1a, but they can make it hard to edit a file
|
8
|
+
#
|
9
|
+
# Arguments: none
|
10
|
+
#
|
11
|
+
# Usage:
|
12
|
+
#
|
13
|
+
# class MyPreflight
|
14
|
+
# include Preflight::Profile
|
15
|
+
#
|
16
|
+
# rule Preflight::Rules::NoFontSubsets
|
17
|
+
# end
|
9
18
|
#
|
10
19
|
class NoFontSubsets
|
11
20
|
|
12
|
-
def
|
21
|
+
def check_hash(ohash)
|
13
22
|
array = []
|
14
23
|
ohash.each do |key, obj|
|
15
24
|
next unless obj.is_a?(::Hash) && obj[:Type] == :Font
|
16
25
|
if subset?(obj)
|
17
|
-
array << "Font #{obj[:BaseFont]}
|
26
|
+
array << Issue.new("Font partially subseted (#{obj[:BaseFont]})", self, :base_font => obj[:BaseFont])
|
18
27
|
end
|
19
28
|
end
|
20
29
|
array
|
@@ -23,7 +32,7 @@ module Preflight
|
|
23
32
|
private
|
24
33
|
|
25
34
|
def subset?(font)
|
26
|
-
font[:BaseFont] && font[:BaseFont].
|
35
|
+
font[:BaseFont] && font[:BaseFont].to_s[/.+\+.+/]
|
27
36
|
end
|
28
37
|
end
|
29
38
|
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
module Preflight
|
6
|
+
module Rules
|
7
|
+
|
8
|
+
# Some print workflows forbid the use of Gray colour.
|
9
|
+
#
|
10
|
+
# Arguments: none
|
11
|
+
#
|
12
|
+
# Usage:
|
13
|
+
#
|
14
|
+
# class MyPreflight
|
15
|
+
# include Preflight::Profile
|
16
|
+
#
|
17
|
+
# rule Preflight::Rules::NoGray
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
class NoGray
|
21
|
+
extend Forwardable
|
22
|
+
|
23
|
+
# Graphics State Operators
|
24
|
+
def_delegators :@state, :save_graphics_state, :restore_graphics_state
|
25
|
+
|
26
|
+
# Matrix Operators
|
27
|
+
def_delegators :@state, :concatenate_matrix
|
28
|
+
|
29
|
+
attr_reader :issues
|
30
|
+
|
31
|
+
# we're about to start a new page, reset state
|
32
|
+
#
|
33
|
+
def page=(page)
|
34
|
+
@page = page
|
35
|
+
@state = PDF::Reader::PageState.new(page)
|
36
|
+
@issues = []
|
37
|
+
@resource_labels_seen = []
|
38
|
+
end
|
39
|
+
|
40
|
+
# descend into nested form xobjects
|
41
|
+
#
|
42
|
+
def invoke_xobject(label)
|
43
|
+
@state.invoke_xobject(label) do |xobj|
|
44
|
+
case xobj
|
45
|
+
when PDF::Reader::FormXObject then
|
46
|
+
xobj.walk(self)
|
47
|
+
when PDF::Reader::Stream then
|
48
|
+
check_xobject(xobj)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def set_gray_for_stroking(g)
|
54
|
+
gray_detected(g)
|
55
|
+
end
|
56
|
+
|
57
|
+
def set_gray_for_nonstroking(g)
|
58
|
+
gray_detected(g)
|
59
|
+
end
|
60
|
+
|
61
|
+
def set_stroke_color_space(label)
|
62
|
+
check_color_space(label)
|
63
|
+
end
|
64
|
+
|
65
|
+
def set_nonstroke_color_space(label)
|
66
|
+
check_color_space(label)
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def color_space_is_gray?(cs)
|
72
|
+
case cs
|
73
|
+
when Symbol then cs == :DeviceGray
|
74
|
+
when Array then
|
75
|
+
cs[0] == :DeviceGray || cs[2] == :DeviceGray
|
76
|
+
else
|
77
|
+
false
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def check_color_space(label)
|
82
|
+
return if @resource_labels_seen.include?(label)
|
83
|
+
|
84
|
+
if color_space_is_gray?(@state.find_color_space(label))
|
85
|
+
@issues << Issue.new("Gray color detected", self, :page => @page.number)
|
86
|
+
end
|
87
|
+
|
88
|
+
@resource_labels_seen << label
|
89
|
+
end
|
90
|
+
|
91
|
+
def check_xobject(xobject)
|
92
|
+
cs = xobject.hash[:ColorSpace]
|
93
|
+
if cs == :DeviceGray
|
94
|
+
@issues << Issue.new("Gray image detected", self, :page => @page.number)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def gray_detected(g)
|
99
|
+
@issues << Issue.new("Gray color detected", self, :page => @page.number,
|
100
|
+
:gray => g)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module Preflight
|
4
|
+
module Rules
|
5
|
+
|
6
|
+
# Ensure the target file contains no page rotation.
|
7
|
+
#
|
8
|
+
# Rotating pages is generally acceptable in most specs but it can cause
|
9
|
+
# rendering issues with poor waulity PDF consumers.
|
10
|
+
#
|
11
|
+
# Arguments: none
|
12
|
+
#
|
13
|
+
# Usage:
|
14
|
+
#
|
15
|
+
# class MyPreflight
|
16
|
+
# include Preflight::Profile
|
17
|
+
#
|
18
|
+
# rule Preflight::Rules::NoPageRotation
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
class NoPageRotation
|
22
|
+
|
23
|
+
attr_reader :issues
|
24
|
+
|
25
|
+
def page=(page)
|
26
|
+
attrs = page.attributes
|
27
|
+
|
28
|
+
if attrs[:Rotate] && attrs[:Rotate] != 0
|
29
|
+
@issues = [Issue.new("Page is rotated", self, :page => page.number)]
|
30
|
+
else
|
31
|
+
@issues = []
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module Preflight
|
4
|
+
module Rules
|
5
|
+
|
6
|
+
# Ensure the target file contains no private application data.
|
7
|
+
#
|
8
|
+
# PDF generating apps (like Adobe Illustrator) can embed their own private
|
9
|
+
# data in saved PDFs. This data is generally harmless and ignored by all
|
10
|
+
# other PDF consumers, but it can lead to large increases in file size.
|
11
|
+
#
|
12
|
+
# Arguments: none
|
13
|
+
#
|
14
|
+
# Usage:
|
15
|
+
#
|
16
|
+
# class MyPreflight
|
17
|
+
# include Preflight::Profile
|
18
|
+
#
|
19
|
+
# rule Preflight::Rules::NoPrivateData
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
class NoPrivateData
|
23
|
+
|
24
|
+
attr_reader :issues
|
25
|
+
|
26
|
+
def page=(page)
|
27
|
+
attrs = page.attributes
|
28
|
+
|
29
|
+
if attrs[:PieceInfo]
|
30
|
+
@issues = [Issue.new("Page contains private PieceInfo data", self, :page => page.number)]
|
31
|
+
else
|
32
|
+
@issues = []
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
module Preflight
|
6
|
+
module Rules
|
7
|
+
|
8
|
+
# Some print workflows forbid the use of Registration Black
|
9
|
+
#
|
10
|
+
# Arguments: none
|
11
|
+
#
|
12
|
+
# Usage:
|
13
|
+
#
|
14
|
+
# class MyPreflight
|
15
|
+
# include Preflight::Profile
|
16
|
+
#
|
17
|
+
# rule Preflight::Rules::NoRegistrationBlack
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
class NoRegistrationBlack
|
21
|
+
extend Forwardable
|
22
|
+
|
23
|
+
# Graphics State Operators
|
24
|
+
def_delegators :@state, :save_graphics_state, :restore_graphics_state
|
25
|
+
|
26
|
+
# Matrix Operators
|
27
|
+
def_delegators :@state, :concatenate_matrix
|
28
|
+
|
29
|
+
attr_reader :issues
|
30
|
+
|
31
|
+
# we're about to start a new page, reset state
|
32
|
+
#
|
33
|
+
def page=(page)
|
34
|
+
@page = page
|
35
|
+
@state = PDF::Reader::PageState.new(page)
|
36
|
+
@issues = []
|
37
|
+
@resource_labels_seen = []
|
38
|
+
end
|
39
|
+
|
40
|
+
# descend into nested form xobjects
|
41
|
+
#
|
42
|
+
def invoke_xobject(label)
|
43
|
+
@state.invoke_xobject(label) do |xobj|
|
44
|
+
case xobj
|
45
|
+
when PDF::Reader::FormXObject then
|
46
|
+
xobj.walk(self)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def set_cmyk_color_for_stroking(c, m, y, k)
|
52
|
+
reg_black_detected(c, m, y, k) if registration_black?(c, m, y, k)
|
53
|
+
end
|
54
|
+
|
55
|
+
def set_cmyk_color_for_nonstroking(c, m, y, k)
|
56
|
+
reg_black_detected(c, m, y, k) if registration_black?(c, m, y, k)
|
57
|
+
end
|
58
|
+
|
59
|
+
def set_stroke_color_space(label)
|
60
|
+
check_color_space(label)
|
61
|
+
end
|
62
|
+
|
63
|
+
def set_nonstroke_color_space(label)
|
64
|
+
check_color_space(label)
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def registration_black?(c, m, y, k)
|
70
|
+
c == 1 && m == 1 && y == 1 && k == 1
|
71
|
+
end
|
72
|
+
|
73
|
+
def color_space_is_all?(cs)
|
74
|
+
cs.is_a?(Array) && cs[1].to_s.downcase == "all"
|
75
|
+
end
|
76
|
+
|
77
|
+
def check_color_space(label)
|
78
|
+
return if @resource_labels_seen.include?(label)
|
79
|
+
|
80
|
+
if color_space_is_all?(@state.find_color_space(label))
|
81
|
+
@issues << Issue.new("'All' separation color detected", self, :page => @page.number)
|
82
|
+
end
|
83
|
+
|
84
|
+
@resource_labels_seen << label
|
85
|
+
end
|
86
|
+
|
87
|
+
def reg_black_detected(c, m, y, k)
|
88
|
+
@issues << Issue.new("Registration black detected", self, :page => @page.number,
|
89
|
+
:cyan => c,
|
90
|
+
:magenta => m,
|
91
|
+
:yellow => y,
|
92
|
+
:k => k)
|
93
|
+
end
|
94
|
+
|
95
|
+
def deref(obj)
|
96
|
+
@page.objects.deref(obj)
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
end
|