clockworkcomicpdf 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|