clockworkcomicpdf 0.0.1
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.
- checksums.yaml +7 -0
- data/lib/clockwork_comic_pdf/book.rb +102 -0
- data/lib/clockwork_comic_pdf/book_init.rb +37 -0
- data/lib/clockwork_comic_pdf/book_validation.rb +79 -0
- data/lib/clockwork_comic_pdf/cover.rb +42 -0
- data/lib/clockwork_comic_pdf/errors.rb +13 -0
- data/lib/clockwork_comic_pdf/measurement_parser.rb +83 -0
- data/lib/clockwork_comic_pdf/option_validation.rb +23 -0
- data/lib/clockwork_comic_pdf/page_header.rb +59 -0
- data/lib/clockwork_comic_pdf/pdf_maker.rb +116 -0
- data/lib/clockwork_comic_pdf/pdf_section_maker.rb +82 -0
- data/lib/clockwork_comic_pdf/pdf_toc_maker.rb +27 -0
- data/lib/clockwork_comic_pdf/section.rb +144 -0
- data/lib/clockwork_comic_pdf/to_points.rb +27 -0
- data/lib/clockwork_comic_pdf/version.rb +59 -0
- data/lib/clockworkcomicpdf.rb +21 -0
- metadata +72 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 983d2f02eac2ae6b01baf045ccae832effa3ae3a
|
|
4
|
+
data.tar.gz: 565431e7815bcfd34301b89b45b3b6873cb8a0a6
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 034150b51ff58647e31ba36f77a0916bd3290984e153fffcaca1b8c3acac180a4f2b4f57d3abc29d961efd9dd356d77220b3cd2e877d8cf42a3971a4a9e1502a
|
|
7
|
+
data.tar.gz: 0a6aefc0b8080189f0b6a9e57c12b5a3126d1c398531e1805856717f2a0589821760d613a95553b4e795a5005d047caf58e8ccd4fb40c062ebbe3a232e2aab22
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
require_relative 'to_points'
|
|
2
|
+
require_relative 'page_header'
|
|
3
|
+
require_relative 'errors'
|
|
4
|
+
require_relative 'cover'
|
|
5
|
+
require_relative 'version'
|
|
6
|
+
require_relative 'option_validation'
|
|
7
|
+
require_relative 'book_validation'
|
|
8
|
+
require_relative 'book_init'
|
|
9
|
+
|
|
10
|
+
module ClockworkComicPDF
|
|
11
|
+
# variable storage for the book class
|
|
12
|
+
class Book
|
|
13
|
+
include OptionValidation, BookValidation, BookInit
|
|
14
|
+
def font
|
|
15
|
+
@font ||= 'Helvetica'
|
|
16
|
+
end
|
|
17
|
+
attr_writer :font
|
|
18
|
+
|
|
19
|
+
def font_size
|
|
20
|
+
@font_size ||= 12
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def font_size=(font_size)
|
|
24
|
+
@font_size = font_size.to_points unless font_size.nil?
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
attr_accessor :base_file_name
|
|
28
|
+
|
|
29
|
+
def info
|
|
30
|
+
@info ||= {}
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def info=(info)
|
|
34
|
+
if info.is_a? Hash
|
|
35
|
+
info[:CreationDate] = Time.now
|
|
36
|
+
info[:Creator] = 'Clockwork Comic PDF Engine'
|
|
37
|
+
info[:Producer] = 'Prawn'
|
|
38
|
+
else
|
|
39
|
+
fail InvalidValueError, 'Information must be set as a Hash value'
|
|
40
|
+
end
|
|
41
|
+
@info = info
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def page_size
|
|
45
|
+
@page_size ||= [{ val: 8.5, type: :in },
|
|
46
|
+
{ val: 11, type: :in }]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def page_size=(page_size)
|
|
50
|
+
page_size = page_size.to_a
|
|
51
|
+
page_size.each do |dim|
|
|
52
|
+
page_size[page_size.find_index dim] = dim.to_points
|
|
53
|
+
end
|
|
54
|
+
@page_size = page_size
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def margin
|
|
58
|
+
@margin ||= { val: 0.25, type: :in }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def margin=(margin)
|
|
62
|
+
@margin = []
|
|
63
|
+
margin = [margin] unless margin.is_a? Array
|
|
64
|
+
unless margin.count == 4 || margin.count == 1
|
|
65
|
+
fail InvalidValueError 'Margin must contain 1 or 4 values'
|
|
66
|
+
end
|
|
67
|
+
margin.each_index do |i|
|
|
68
|
+
@margin[i] = margin[i].to_points
|
|
69
|
+
end
|
|
70
|
+
@margin
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def offset_from_spine
|
|
74
|
+
@offset_from_spine ||= 0
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def offset_from_spine=(offset_from_spine)
|
|
78
|
+
@offset_from_spine = offset_from_spine.to_points
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def print_toc
|
|
82
|
+
@print_toc = true if @print_toc.nil?
|
|
83
|
+
@print_toc
|
|
84
|
+
end
|
|
85
|
+
attr_writer :print_toc
|
|
86
|
+
|
|
87
|
+
def print_pagenum
|
|
88
|
+
@print_pagenum = true if @print_toc.nil?
|
|
89
|
+
@print_pagenum
|
|
90
|
+
end
|
|
91
|
+
attr_writer :print_pagenum
|
|
92
|
+
attr_accessor :page_header
|
|
93
|
+
attr_accessor :cover
|
|
94
|
+
|
|
95
|
+
def versions
|
|
96
|
+
@versions ||= []
|
|
97
|
+
end
|
|
98
|
+
attr_writer :versions
|
|
99
|
+
|
|
100
|
+
attr_accessor :sections
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module ClockworkComicPDF
|
|
2
|
+
# initalization blocks for the book class
|
|
3
|
+
module BookInit
|
|
4
|
+
def valid_options
|
|
5
|
+
[:font, :info, :base_file_name, :page_size, :margin,
|
|
6
|
+
:offset_from_spine, :print_toc, :print_pagenum, :page_header,
|
|
7
|
+
:versions, :sections, :font_size, :cover]
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def initialize(options = {})
|
|
11
|
+
check_options(options.keys, valid_options)
|
|
12
|
+
initialize_options(options)
|
|
13
|
+
initialize_content(options)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def initialize_options(options = {})
|
|
19
|
+
self.font = options[:font]
|
|
20
|
+
self.base_file_name = options[:base_file_name]
|
|
21
|
+
self.info = options[:info]
|
|
22
|
+
self.page_size = options[:page_size]
|
|
23
|
+
self.margin = options[:margin]
|
|
24
|
+
self.offset_from_spine = options[:offset_from_spine]
|
|
25
|
+
self.print_toc = options[:print_toc]
|
|
26
|
+
self.print_pagenum = options[:print_pagenum]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def initialize_content(options = {})
|
|
30
|
+
self.page_header = PageHeader.new(options[:page_header])
|
|
31
|
+
self.cover = Cover.new(options[:cover])
|
|
32
|
+
self.versions = Versions.new(options[:versions])
|
|
33
|
+
self.sections = Sections.new(options[:sections])
|
|
34
|
+
self.font_size = options[:font_size]
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
module ClockworkComicPDF
|
|
2
|
+
# Validation Module for Book Class
|
|
3
|
+
module BookValidation
|
|
4
|
+
def validate
|
|
5
|
+
fail MissingValueError, 'base_file_name required' unless @base_file_name
|
|
6
|
+
cover.validate if cover
|
|
7
|
+
sections.validate
|
|
8
|
+
versions.validate
|
|
9
|
+
miss = image_mismatch
|
|
10
|
+
if miss.count > 0
|
|
11
|
+
fail_s = "Image directories do no match: #{miss.join(', ')}"
|
|
12
|
+
fail MissingValueError, fail_s
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def image_mismatch
|
|
17
|
+
mismatch = []
|
|
18
|
+
files = version_files
|
|
19
|
+
files.each_pair do |key_v, sec|
|
|
20
|
+
mismatch.concat(scan_version(key_v, sec, files))
|
|
21
|
+
end
|
|
22
|
+
mismatch.flatten
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def scan_version(key_v, sec, files)
|
|
26
|
+
mismatch = []
|
|
27
|
+
files.each_pair do |c_ver, c_sec|
|
|
28
|
+
unless key_v == c_ver
|
|
29
|
+
mismatch << scan_section(key_v.name, sec, c_ver.name, c_sec)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
mismatch
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def scan_section(v_name, sec_v, c_name, sec_c)
|
|
36
|
+
mismatch = []
|
|
37
|
+
sec_v.each_pair do |ss_key, ss_v|
|
|
38
|
+
ss_v.each do |f|
|
|
39
|
+
unless sec_c[ss_key].include? f
|
|
40
|
+
mismatch << "#{f} in section #{ss_key.name} exists " <<
|
|
41
|
+
"in #{v_name} but not #{c_name}"
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
mismatch
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def version_files
|
|
49
|
+
files = {}
|
|
50
|
+
versions.each do |version|
|
|
51
|
+
files[version] = section_files(version)
|
|
52
|
+
end
|
|
53
|
+
files
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def section_files(check_ver)
|
|
57
|
+
s_files = {}
|
|
58
|
+
[sections.front_matter, sections.body,
|
|
59
|
+
sections.end_matter].flatten.each do |sub|
|
|
60
|
+
if sub.is_a? SectionComicPages
|
|
61
|
+
s_files[sub] = get_comic_files(check_ver, sub)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
s_files
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def get_comic_files(check_ver, sub)
|
|
68
|
+
current_dir = "./#{check_ver.name}/#{sub.directory}/"
|
|
69
|
+
unless File.exist?(current_dir)
|
|
70
|
+
fail InvalidValueError, "#{File.path(current_dir)} does not exist"
|
|
71
|
+
end
|
|
72
|
+
c_files = []
|
|
73
|
+
Dir["#{current_dir}*.*"].each do |file|
|
|
74
|
+
c_files << File.basename(file, '.*')
|
|
75
|
+
end
|
|
76
|
+
c_files
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require_relative 'to_points'
|
|
2
|
+
require_relative 'errors'
|
|
3
|
+
require_relative 'option_validation'
|
|
4
|
+
|
|
5
|
+
module ClockworkComicPDF
|
|
6
|
+
# this contains the size, file path, and size of the cover
|
|
7
|
+
class Cover
|
|
8
|
+
include OptionValidation
|
|
9
|
+
attr_reader :size
|
|
10
|
+
def size=(size)
|
|
11
|
+
unless size.is_a?(Array) && size.count == 2
|
|
12
|
+
throw InvalidValueError.new 'Cover size must contain exactly 2 values'
|
|
13
|
+
end
|
|
14
|
+
size[0] = size[0].to_points
|
|
15
|
+
size[1] = size[1].to_points
|
|
16
|
+
@size = size
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
attr_reader :path
|
|
20
|
+
def path=(path)
|
|
21
|
+
@path = path.to_s
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
attr_reader :file
|
|
25
|
+
def file=(file)
|
|
26
|
+
@file = file.to_s
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def validate
|
|
30
|
+
fail InvalidValueError, 'Cover Must specify a file' unless file
|
|
31
|
+
fail InvalidValueError, 'Cover must specify a size' unless size
|
|
32
|
+
fail InvalidValueError, 'Cover must specify a path' unless path
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def initialize(options = {})
|
|
36
|
+
check_options(options.keys, [:size, :path, :file])
|
|
37
|
+
self.path = options[:path]
|
|
38
|
+
self.size = options[:size]
|
|
39
|
+
self.file = options[:file]
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
module ClockworkComicPDF
|
|
2
|
+
# stores the option parsing logic for PDFMaker
|
|
3
|
+
module MeasurementParser
|
|
4
|
+
def parse_options(pdf, opt_in)
|
|
5
|
+
out = {}
|
|
6
|
+
opt_in.each_pair do |key, val|
|
|
7
|
+
case key
|
|
8
|
+
when :height_ratio then out[:height] = pdf.bounds.height * val.to_r
|
|
9
|
+
when :width_ratio then out[:width] = pdf.bounds.width * val.to_r
|
|
10
|
+
else out[key] = convert_val(pdf, val, opt_in)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
out
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def convert_val(pdf, val, opt_in)
|
|
17
|
+
if val.is_a? Array
|
|
18
|
+
val = Array.new(val)
|
|
19
|
+
val.each_index do |i|
|
|
20
|
+
val[i] = convert_val(pdf, val[i], opt_in)
|
|
21
|
+
end
|
|
22
|
+
else
|
|
23
|
+
val = convert_position(pdf, val)
|
|
24
|
+
val = convert_size(pdf, val, opt_in)
|
|
25
|
+
end
|
|
26
|
+
val
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def convert_position(pdf, val)
|
|
30
|
+
val = convert_complex_position(pdf, val)
|
|
31
|
+
val = convert_simple_position(pdf, val)
|
|
32
|
+
val
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def convert_complex_position(pdf, val)
|
|
36
|
+
case val
|
|
37
|
+
when :bounds_top_left then return pdf.bounds.top_left
|
|
38
|
+
when :bounds_top_right then return pdf.bounds.top_right
|
|
39
|
+
when :bounds_bottom_left then return pdf.bounds.bottom_left
|
|
40
|
+
when :bounds_bottom_right then return pdf.bounds.bottom_right
|
|
41
|
+
else return val
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def convert_simple_position(pdf, val)
|
|
46
|
+
case val
|
|
47
|
+
when :bounds_top then return pdf.bounds.top
|
|
48
|
+
when :bounds_bottom then return pdf.bounds.bottom
|
|
49
|
+
when :bounds_left then return pdf.bounds.left
|
|
50
|
+
when :bounds_right then return pdf.bounds.right
|
|
51
|
+
else return val
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def convert_size(pdf, val, opt_in)
|
|
56
|
+
case val
|
|
57
|
+
when :bounds_width then return pdf.bounds.width
|
|
58
|
+
when :bounds_height then return pdf.bounds.height
|
|
59
|
+
when :bounds_center_width then return get_h_center(pdf, opt_in)
|
|
60
|
+
when :bounds_center_height then return get_v_center(pdf, opt_in)
|
|
61
|
+
else return val
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def get_h_center(pdf, opt_in)
|
|
66
|
+
if !opt_in[:width_ratio].nil?
|
|
67
|
+
box_width = opt_in[:width_ratio].to_r * pdf.bounds.width
|
|
68
|
+
else
|
|
69
|
+
box_width = opt_in[:width]
|
|
70
|
+
end
|
|
71
|
+
pdf.bounds.width / 2 - box_width / 2
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def get_v_center(pdf, options)
|
|
75
|
+
if !opt_in[:height_ratio].nil?
|
|
76
|
+
box_height = opt_in[:height_ratio].to_r * pdf.bounds.height
|
|
77
|
+
else
|
|
78
|
+
box_height = opt_in[:height]
|
|
79
|
+
end
|
|
80
|
+
pdf.bounds.height / 2 - box_height / 2
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require_relative 'errors'
|
|
2
|
+
|
|
3
|
+
# Module-level parameter checker
|
|
4
|
+
module ClockworkComicPDF
|
|
5
|
+
# provides option validation and requirements vaildaiton
|
|
6
|
+
module OptionValidation
|
|
7
|
+
def check_options(options, valid_options)
|
|
8
|
+
options.each do |key|
|
|
9
|
+
unless valid_options.include? key
|
|
10
|
+
fail UndefinedKeyError, "Unsupported key '#{key}' " <<
|
|
11
|
+
"for '#{self.class}'"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def check_required(options = {})
|
|
17
|
+
options.each_pair do |key, val|
|
|
18
|
+
fail UndefinedKeyError, "'#{self.class}' requires " <<
|
|
19
|
+
"key '#{key}'" if val.nil?
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
require_relative 'errors'
|
|
2
|
+
require_relative 'option_validation'
|
|
3
|
+
|
|
4
|
+
module ClockworkComicPDF
|
|
5
|
+
# storage class for the page header
|
|
6
|
+
# includes alignment, left and right side text, and size
|
|
7
|
+
class PageHeader
|
|
8
|
+
include OptionValidation
|
|
9
|
+
def align
|
|
10
|
+
@align = :center unless @align
|
|
11
|
+
@align
|
|
12
|
+
end
|
|
13
|
+
attr_writer :align
|
|
14
|
+
|
|
15
|
+
def text=(text)
|
|
16
|
+
self.left_text = text
|
|
17
|
+
self.right_text = text
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def text
|
|
21
|
+
{ left: left_text, right: right_text }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def left_text
|
|
25
|
+
@left_text ||= ''
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def left_text=(left_text)
|
|
29
|
+
@left_text = left_text.to_s
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def right_text
|
|
33
|
+
@right_text ||= ''
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def right_text=(right_text)
|
|
37
|
+
@right_text = right_text.to_s
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def size
|
|
41
|
+
@size = 8 unless @size
|
|
42
|
+
@size
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def size=(size)
|
|
46
|
+
@size = size.to_points
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def initialize(options = {})
|
|
50
|
+
valid_options = [:size, :text, :left_text, :right_text, :align]
|
|
51
|
+
check_options(options.keys, valid_options)
|
|
52
|
+
self.size = options[:size]
|
|
53
|
+
self.text = options[:text]
|
|
54
|
+
self.left_text = options[:left_text]
|
|
55
|
+
self.right_text = options[:right_text]
|
|
56
|
+
self.align = options[:align]
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
require_relative 'book'
|
|
2
|
+
require_relative 'version'
|
|
3
|
+
require_relative 'measurement_parser'
|
|
4
|
+
require_relative 'pdf_section_maker'
|
|
5
|
+
require_relative 'pdf_toc_maker'
|
|
6
|
+
require 'prawn'
|
|
7
|
+
|
|
8
|
+
module ClockworkComicPDF
|
|
9
|
+
# this parses the sections of a book into a pdf file
|
|
10
|
+
class PDFMaker
|
|
11
|
+
include MeasurementParser, PDFSectionMaker, PDFTocMaker
|
|
12
|
+
attr_accessor :book, :content_start, :debug, :printing_body, :current_page,
|
|
13
|
+
:trim_offset
|
|
14
|
+
|
|
15
|
+
attr_writer :page_index
|
|
16
|
+
def page_index
|
|
17
|
+
@page_index ||= []
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def initialize(book)
|
|
21
|
+
self.book = book
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def print
|
|
25
|
+
book.validate
|
|
26
|
+
book.versions.each do |version|
|
|
27
|
+
self.content_start = nil
|
|
28
|
+
self.printing_body = false
|
|
29
|
+
self.current_page = nil
|
|
30
|
+
self.page_index = nil
|
|
31
|
+
self.debug = false
|
|
32
|
+
print_version(version)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def print_version(version)
|
|
37
|
+
self.trim_offset = version.trim_offset
|
|
38
|
+
pdf = Prawn::Document.new(info: book.info, font_size: book.font_size,
|
|
39
|
+
skip_page_creation: true)
|
|
40
|
+
print_sections(pdf, version)
|
|
41
|
+
print_cover(pdf, version) if version.print_cover
|
|
42
|
+
pdf.render_file "#{book.base_file_name} - #{version.name}.pdf"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def print_cover(pdf, version)
|
|
46
|
+
pdf.go_to_page 0
|
|
47
|
+
return if book.cover.nil?
|
|
48
|
+
cover = book.cover
|
|
49
|
+
pdf.start_new_page(size: cover.size, margin: 0)
|
|
50
|
+
pdf.image("#{cover.path}/#{version.name}/#{cover.file}",
|
|
51
|
+
at: pdf.bounds.top_left, scale: 72.0 / version.dpi.to_f)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def new_page(pdf)
|
|
55
|
+
if trim_offset then make_trim_page(pdf)
|
|
56
|
+
else make_offset_page(pdf)
|
|
57
|
+
end
|
|
58
|
+
pdf.font(book.font)
|
|
59
|
+
print_body_page(pdf) if printing_body
|
|
60
|
+
debug_stroke(pdf) if debug
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def make_offset_page(pdf)
|
|
64
|
+
margin = Array.new(book.margin)
|
|
65
|
+
if pdf.page_number.odd? then margin[1] += book.offset_from_spine
|
|
66
|
+
else margin[3] += book.offset_from_spine
|
|
67
|
+
end
|
|
68
|
+
pdf.start_new_page(size: book.page_size, margin: margin)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def make_trim_page(pdf)
|
|
72
|
+
margin = Array.new(book.margin)
|
|
73
|
+
pdf.start_new_page(size: [book.page_size[0] - book.offset_from_spine,
|
|
74
|
+
book.page_size[1]],
|
|
75
|
+
margin: margin)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def print_body_page(pdf)
|
|
79
|
+
self.current_page += 1
|
|
80
|
+
print_header(pdf) unless book.page_header.nil?
|
|
81
|
+
print_page_num(pdf) if book.print_pagenum
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def print_header(pdf)
|
|
85
|
+
head = book.page_header
|
|
86
|
+
options = { size: head.size, align: head.align, width: pdf.bounds.width }
|
|
87
|
+
text = pdf.page_number.even? ? head.left_text : head.right_text
|
|
88
|
+
options[:at] = [pdf.bounds.left, pdf.bounds.top + 0.25.in]
|
|
89
|
+
options[:valign] = :center
|
|
90
|
+
options[:height] = 0.25.in
|
|
91
|
+
if options[:align] == :alternating
|
|
92
|
+
options[:align] = even_page ? :left : :right
|
|
93
|
+
end
|
|
94
|
+
pdf.text_box(text, options)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def print_page_num(pdf)
|
|
98
|
+
options = { at: pdf.bounds.bottom_left,
|
|
99
|
+
width: pdf.bounds.width,
|
|
100
|
+
align: :center,
|
|
101
|
+
size: 8 }
|
|
102
|
+
options[:height] = 0.25.in
|
|
103
|
+
pdf.text_box("#{self.current_page}", options)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def debug_stroke(pdf)
|
|
107
|
+
pdf.stroke_bounds
|
|
108
|
+
pdf.stroke do
|
|
109
|
+
pdf.line [0, 0], [pdf.bounds.width, pdf.bounds.height]
|
|
110
|
+
pdf.line [pdf.bounds.width, 0], [0, pdf.bounds.height]
|
|
111
|
+
pdf.line [pdf.bounds.width / 2,
|
|
112
|
+
pdf.bounds.height], [pdf.bounds.width / 2, 0]
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
module ClockworkComicPDF
|
|
2
|
+
# this parses the sections of a book into a pdf file
|
|
3
|
+
module PDFSectionMaker
|
|
4
|
+
def print_sections(pdf, version)
|
|
5
|
+
print_section(pdf, version, book.sections.front_matter)
|
|
6
|
+
self.printing_body = true
|
|
7
|
+
self.current_page = 0
|
|
8
|
+
self.content_start = pdf.page_number + 1
|
|
9
|
+
print_section(pdf, version, book.sections.body)
|
|
10
|
+
self.printing_body = false
|
|
11
|
+
print_section(pdf, version, book.sections.end_matter)
|
|
12
|
+
new_page(pdf) if pdf.page_number.odd?
|
|
13
|
+
print_toc(pdf) if book.print_toc
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def print_section(pdf, version, section)
|
|
17
|
+
section.each do |item|
|
|
18
|
+
case item.type
|
|
19
|
+
when :text_box
|
|
20
|
+
print_text_box(pdf, version, item)
|
|
21
|
+
when :formatted_text_box
|
|
22
|
+
print_formatted_text_box(pdf, version, item)
|
|
23
|
+
when :comic_pages
|
|
24
|
+
print_comic_pages(pdf, version, item)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def print_comic_pages(pdf, version, comic_pages)
|
|
30
|
+
puts comic_pages.name
|
|
31
|
+
if comic_pages.print_section_intro
|
|
32
|
+
print_section_break(pdf, comic_pages.name)
|
|
33
|
+
end
|
|
34
|
+
section_dir = "./#{version.name}/#{comic_pages.directory}/"
|
|
35
|
+
Dir["#{section_dir}*.*"].each do |image|
|
|
36
|
+
print_comic_image(pdf, version, image)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def print_section_break(pdf, text)
|
|
41
|
+
new_page(pdf)
|
|
42
|
+
pdf.move_cursor_to pdf.bounds.top
|
|
43
|
+
options = { valign: :center, align: :center, width: pdf.bounds.width,
|
|
44
|
+
height: pdf.bounds.height, size: 18,
|
|
45
|
+
at: pdf.bounds.top_left }
|
|
46
|
+
page_index << { page: current_page, name: text } if printing_body
|
|
47
|
+
pdf.text_box(text, options)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def print_comic_image(pdf, version, image)
|
|
51
|
+
new_page(pdf)
|
|
52
|
+
name = make_name(image)
|
|
53
|
+
page_index << { page: current_page, name: "#{name}" } if printing_body
|
|
54
|
+
content = [[name], [{ image: image, scale: 72.0 / version.dpi }]]
|
|
55
|
+
table = Prawn::Table.new(content, pdf, cell_style: { borders: [] },
|
|
56
|
+
position: :center)
|
|
57
|
+
pdf.move_down((pdf.bounds.height - table.height) / 2.0)
|
|
58
|
+
table.draw
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def make_name(file_path)
|
|
62
|
+
File.basename(file_path, '.*').split(' ').slice(1..-1).join(' ')
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def print_text_box(pdf, version, content)
|
|
66
|
+
text = File.new(content.file).read
|
|
67
|
+
make_text_box(pdf, [{ text: text }], content)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def print_formatted_text_box(pdf, version, content)
|
|
71
|
+
text = YAML.load_file(content.file)
|
|
72
|
+
make_text_box(pdf, text, content)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def make_text_box(pdf, text, content)
|
|
76
|
+
new_page(pdf)
|
|
77
|
+
page_index << { page: current_page, name: content.name } if printing_body
|
|
78
|
+
options = parse_options(pdf, content.options)
|
|
79
|
+
pdf.formatted_text_box(text, options)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module ClockworkComicPDF
|
|
2
|
+
# stores the PDF printing methods for making the table of contents
|
|
3
|
+
module PDFTocMaker
|
|
4
|
+
def print_toc(pdf)
|
|
5
|
+
pdf.go_to_page(content_start - 1)
|
|
6
|
+
toc_start = pdf.page_number
|
|
7
|
+
new_page(pdf)
|
|
8
|
+
pdf.text 'Table of Contents', size: 18, align: :left
|
|
9
|
+
pdf.move_down(25)
|
|
10
|
+
page_index.each { |toc_item| make_toc_item(pdf, toc_item) }
|
|
11
|
+
new_page(pdf) unless (toc_start - pdf.page_number).even?
|
|
12
|
+
self.content_start += (toc_start - pdf.page_number)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def make_toc_item(pdf, toc_item)
|
|
16
|
+
content = [["#{toc_item[:name]}", "#{toc_item[:page]}"]]
|
|
17
|
+
options = { width: pdf.bounds.width / 2, position: :center,
|
|
18
|
+
cell_style: { borders: [], size: 8,
|
|
19
|
+
padding: 1 } }
|
|
20
|
+
table = Prawn::Table.new(content, pdf, options) do
|
|
21
|
+
cells.style { |c| c.align = c.column.zero? ? :left : :right }
|
|
22
|
+
end
|
|
23
|
+
new_page(pdf) if table.height >= pdf.cursor
|
|
24
|
+
table.draw
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
require_relative 'option_validation'
|
|
2
|
+
|
|
3
|
+
module ClockworkComicPDF
|
|
4
|
+
# this stores the data for each section of the book
|
|
5
|
+
class Sections
|
|
6
|
+
include OptionValidation
|
|
7
|
+
def front_matter
|
|
8
|
+
@front_matter ||= []
|
|
9
|
+
end
|
|
10
|
+
attr_writer :front_matter
|
|
11
|
+
|
|
12
|
+
def body
|
|
13
|
+
@body ||= []
|
|
14
|
+
@body
|
|
15
|
+
end
|
|
16
|
+
attr_writer :body
|
|
17
|
+
|
|
18
|
+
def end_matter
|
|
19
|
+
@end_matter ||= []
|
|
20
|
+
@end_matter
|
|
21
|
+
end
|
|
22
|
+
attr_writer :end_matter
|
|
23
|
+
|
|
24
|
+
def parse_sections(sections)
|
|
25
|
+
parsed_sections = []
|
|
26
|
+
sections.each do |section|
|
|
27
|
+
parsed_sections << section_by_type(section)
|
|
28
|
+
end
|
|
29
|
+
parsed_sections
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def section_by_type(section)
|
|
33
|
+
case section[:type]
|
|
34
|
+
when :comic_pages
|
|
35
|
+
return SectionComicPages.new(section)
|
|
36
|
+
when :text_box
|
|
37
|
+
return SectionTextBox.new(section)
|
|
38
|
+
when :formatted_text_box
|
|
39
|
+
return SectionFormattedTextBox.new(section)
|
|
40
|
+
else
|
|
41
|
+
fail InvalidKeyError, "#{section[:type]} is not a valid section type"
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def validate
|
|
46
|
+
[front_matter, body, end_matter].each do |chunk|
|
|
47
|
+
chunk.each { |sec| sec.validate }
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def print_pdf(options = {})
|
|
52
|
+
[front_matter, body, end_matter].each do |chunk|
|
|
53
|
+
chunk.each { |sec| sec.print_pdf(options) }
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def initialize(options = {})
|
|
58
|
+
check_options(options.keys, [:front_matter, :body, :end_matter])
|
|
59
|
+
self.front_matter = parse_sections(options[:front_matter])
|
|
60
|
+
self.body = parse_sections(options[:body])
|
|
61
|
+
self.end_matter = parse_sections(options[:end_matter])
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# this is the base class for sections data that stores common section info
|
|
66
|
+
class Section
|
|
67
|
+
include OptionValidation
|
|
68
|
+
attr_writer :print_section_intro
|
|
69
|
+
def print_section_intro
|
|
70
|
+
@print_section_intro = false if @print_section_intro.nil?
|
|
71
|
+
@print_section_intro
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
attr_accessor :type
|
|
75
|
+
attr_accessor :name
|
|
76
|
+
|
|
77
|
+
def valid_options
|
|
78
|
+
@valid_options = [:print_section_intro, :type, :name]
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def req_keys
|
|
82
|
+
@required_keys = { name: @name }
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def validate
|
|
86
|
+
check_required(req_keys)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def initialize(options = {})
|
|
90
|
+
self.name = options[:name]
|
|
91
|
+
self.type = options[:type]
|
|
92
|
+
self.print_section_intro = options[:print_section_intro]
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Comic Pages section
|
|
97
|
+
class SectionComicPages < Section
|
|
98
|
+
def valid_options
|
|
99
|
+
super
|
|
100
|
+
@valid_options << :directory
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def req_keys
|
|
104
|
+
super
|
|
105
|
+
@required_keys[:directory] = @directory
|
|
106
|
+
@required_keys
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
attr_accessor :directory
|
|
110
|
+
|
|
111
|
+
def initialize(options = {})
|
|
112
|
+
super
|
|
113
|
+
self.directory = options[:directory]
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# text box method
|
|
118
|
+
class SectionTextBox < Section
|
|
119
|
+
attr_accessor :file, :options
|
|
120
|
+
|
|
121
|
+
def valid_options
|
|
122
|
+
super
|
|
123
|
+
@valid_options << :file
|
|
124
|
+
@valid_options << :options
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def req_keys
|
|
128
|
+
super
|
|
129
|
+
@required_keys[:file] = @file
|
|
130
|
+
@required_keys[:options] = @options
|
|
131
|
+
@required_keys
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def initialize(options = {})
|
|
135
|
+
super
|
|
136
|
+
self.options = options[:options]
|
|
137
|
+
self.file = options[:file]
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# formatted text box method
|
|
142
|
+
class SectionFormattedTextBox < SectionTextBox
|
|
143
|
+
end
|
|
144
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'prawn/measurement_extensions'
|
|
2
|
+
|
|
3
|
+
# adds point conversion function for hash values
|
|
4
|
+
class Hash
|
|
5
|
+
def to_points
|
|
6
|
+
if size == 2 && self[:val] && self[:type]
|
|
7
|
+
return self[:val].to_f.send(self[:type])
|
|
8
|
+
end
|
|
9
|
+
puts self
|
|
10
|
+
fail ClockworkComicPDF::UndefinedKeyError,
|
|
11
|
+
'measurement must contain only :val and :type'
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# returns itself as is for fixnum
|
|
16
|
+
class Fixnum
|
|
17
|
+
def to_points
|
|
18
|
+
self
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# returns itself as is for Float
|
|
23
|
+
class Float
|
|
24
|
+
def to_points
|
|
25
|
+
self
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
require_relative 'option_validation'
|
|
2
|
+
|
|
3
|
+
module ClockworkComicPDF
|
|
4
|
+
# convienience class for parsing and holding version objects
|
|
5
|
+
class Versions < Array
|
|
6
|
+
def initialize(options)
|
|
7
|
+
options.each do |param|
|
|
8
|
+
self << Version.new(param)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def validate
|
|
13
|
+
each { |version| version.validate }
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# stores version information for PDF generation
|
|
18
|
+
# includes name, length, dpi and a toggle for printing the cover
|
|
19
|
+
class Version
|
|
20
|
+
include OptionValidation
|
|
21
|
+
attr_reader :name
|
|
22
|
+
def name=(name)
|
|
23
|
+
@name = name.to_s
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
attr_reader :dpi
|
|
27
|
+
def dpi=(dpi)
|
|
28
|
+
@dpi = dpi.to_i
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def print_cover
|
|
32
|
+
@print_cover = false if @print_cover.nil?
|
|
33
|
+
@print_cover
|
|
34
|
+
end
|
|
35
|
+
attr_writer :print_cover
|
|
36
|
+
|
|
37
|
+
def trim_offset
|
|
38
|
+
@trim_offset = false if @trim_offset.nil?
|
|
39
|
+
@trim_offset
|
|
40
|
+
end
|
|
41
|
+
attr_writer :trim_offset
|
|
42
|
+
|
|
43
|
+
def validate
|
|
44
|
+
fail InvalidValueError, 'Each version must contain a name' unless name
|
|
45
|
+
fail InvalidValueError,
|
|
46
|
+
'Each version must contain a name' if name.length == 0
|
|
47
|
+
fail InvalidValueError,
|
|
48
|
+
'each version must specify a dpi value' unless dpi
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def initialize(options = {})
|
|
52
|
+
check_options(options.keys, [:name, :dpi, :print_cover, :trim_offset])
|
|
53
|
+
self.name = options[:name]
|
|
54
|
+
self.dpi = options[:dpi]
|
|
55
|
+
self.trim_offset = options[:trim_offset]
|
|
56
|
+
self.print_cover = options[:print_cover]
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require 'prawn'
|
|
2
|
+
require 'yaml'
|
|
3
|
+
|
|
4
|
+
require_relative 'clockwork_comic_pdf/book'
|
|
5
|
+
require_relative 'clockwork_comic_pdf/errors'
|
|
6
|
+
require_relative 'clockwork_comic_pdf/page_header'
|
|
7
|
+
require_relative 'clockwork_comic_pdf/cover'
|
|
8
|
+
require_relative 'clockwork_comic_pdf/version'
|
|
9
|
+
require_relative 'clockwork_comic_pdf/section'
|
|
10
|
+
require_relative 'clockwork_comic_pdf/option_validation'
|
|
11
|
+
require_relative 'clockwork_comic_pdf/pdf_maker'
|
|
12
|
+
|
|
13
|
+
# the base module for ClockworkComicPDF
|
|
14
|
+
module ClockworkComicPDF
|
|
15
|
+
# VERSION "0.1.0"
|
|
16
|
+
def book_from_yaml(book)
|
|
17
|
+
parsed_book = Book.new(YAML.load_file(book))
|
|
18
|
+
PDFMaker.new(parsed_book).print
|
|
19
|
+
end
|
|
20
|
+
module_function :book_from_yaml
|
|
21
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: clockworkcomicpdf
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Michael Skiba
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2014-02-27 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: prawn
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ~>
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 0.15.0
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ~>
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: 0.15.0
|
|
27
|
+
description: A Simple(ish) Ruby System for creating print and web ready PDF files.
|
|
28
|
+
email: mike.skiba@atelierclockwork.net
|
|
29
|
+
executables: []
|
|
30
|
+
extensions: []
|
|
31
|
+
extra_rdoc_files: []
|
|
32
|
+
files:
|
|
33
|
+
- lib/clockwork_comic_pdf/book.rb
|
|
34
|
+
- lib/clockwork_comic_pdf/book_init.rb
|
|
35
|
+
- lib/clockwork_comic_pdf/book_validation.rb
|
|
36
|
+
- lib/clockwork_comic_pdf/cover.rb
|
|
37
|
+
- lib/clockwork_comic_pdf/errors.rb
|
|
38
|
+
- lib/clockwork_comic_pdf/measurement_parser.rb
|
|
39
|
+
- lib/clockwork_comic_pdf/option_validation.rb
|
|
40
|
+
- lib/clockwork_comic_pdf/page_header.rb
|
|
41
|
+
- lib/clockwork_comic_pdf/pdf_maker.rb
|
|
42
|
+
- lib/clockwork_comic_pdf/pdf_section_maker.rb
|
|
43
|
+
- lib/clockwork_comic_pdf/pdf_toc_maker.rb
|
|
44
|
+
- lib/clockwork_comic_pdf/section.rb
|
|
45
|
+
- lib/clockwork_comic_pdf/to_points.rb
|
|
46
|
+
- lib/clockwork_comic_pdf/version.rb
|
|
47
|
+
- lib/clockworkcomicpdf.rb
|
|
48
|
+
homepage: http://www.atelierclockwork.net/ccpdf/
|
|
49
|
+
licenses:
|
|
50
|
+
- MIT
|
|
51
|
+
metadata: {}
|
|
52
|
+
post_install_message:
|
|
53
|
+
rdoc_options: []
|
|
54
|
+
require_paths:
|
|
55
|
+
- lib
|
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - '>='
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: 1.9.3
|
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
62
|
+
requirements:
|
|
63
|
+
- - '>='
|
|
64
|
+
- !ruby/object:Gem::Version
|
|
65
|
+
version: 1.3.6
|
|
66
|
+
requirements: []
|
|
67
|
+
rubyforge_project:
|
|
68
|
+
rubygems_version: 2.0.3
|
|
69
|
+
signing_key:
|
|
70
|
+
specification_version: 4
|
|
71
|
+
summary: Clockwork Comic PDF
|
|
72
|
+
test_files: []
|