epuber 0.5.3 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +2 -1
- data/README.md +22 -20
- data/epuber.gemspec +9 -8
- data/lib/epuber/book/target.rb +0 -1
- data/lib/epuber/book/toc_item.rb +0 -1
- data/lib/epuber/checker/text_checker.rb +8 -68
- data/lib/epuber/command.rb +9 -0
- data/lib/epuber/command/build.rb +1 -1
- data/lib/epuber/command/server.rb +1 -1
- data/lib/epuber/compiler/file_resolver.rb +0 -1
- data/lib/epuber/compiler/file_types/xhtml_file.rb +13 -13
- data/lib/epuber/compiler/nav_generator.rb +5 -1
- data/lib/epuber/compiler/opf_generator.rb +16 -4
- data/lib/epuber/compiler/problem.rb +124 -0
- data/lib/epuber/compiler/xhtml_processor.rb +41 -11
- data/lib/epuber/config.rb +14 -2
- data/lib/epuber/dsl/attribute.rb +5 -1
- data/lib/epuber/dsl/attribute_support.rb +2 -6
- data/lib/epuber/dsl/object.rb +0 -5
- data/lib/epuber/server/pages/book.bade +1 -1
- data/lib/epuber/user_interface.rb +7 -1
- data/lib/epuber/version.rb +1 -1
- metadata +39 -26
- data/lib/epuber/ruby_extensions/string.rb +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2899b08ccdd624b08c96377e111df6872f4c6b0871ee689e28d1264d42bf5294
|
4
|
+
data.tar.gz: 6391e4b90172bc5d06d7c3f903aafa52efab3917ee0daaaf406723f3ee2d381f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7a2df3f599de46ca5c5e976154d32752981fa9d2a76e0a46754b91f5583c3e671c3ab77c777bc8511dbda09f23ea0a8534d16656a1dfce4748b762fa7d7c3947
|
7
|
+
data.tar.gz: b5d8d5556ce730d2215a6239405a56505f87e649fa1b06078cb2145f524c2d59e82d2789b21d0873c573079daa807ff9cc3d22b16bf26d85f4e223ac20ead502
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Epuber
|
2
2
|
|
3
|
-
[![Gem Version](https://badge.fury.io/rb/epuber.svg)](http://badge.fury.io/rb/epuber) [![Build Status](https://
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/epuber.svg)](http://badge.fury.io/rb/epuber) [![Build Status](https://github.com/epuber-io/epuber/actions/workflows/tests.yml/badge.svg)](https://github.com/epuber-io/epuber/actions) [![Coverage Status](https://coveralls.io/repos/epuber-io/epuber/badge.svg?branch=master&service=github)](https://coveralls.io/github/epuber-io/epuber?branch=master) [![Inline docs](https://inch-ci.org/github/epuber-io/epuber.svg?branch=master)](https://inch-ci.org/github/epuber-io/epuber)
|
4
4
|
|
5
5
|
Epuber is simple tool to compile and pack source files into EPUB format. Offers to define multiple "targets" which allows to create multiple versions of the book by running only one command. Eliminates copying code and information, eliminates needs to use _git_ branches to differ ebook content.
|
6
6
|
|
@@ -46,37 +46,39 @@ end
|
|
46
46
|
|
47
47
|
Except for paths to plugins `book.use`, every path could be only name of the file, even without extension. So you can rename them, move them, the correct path will always resolved. Only exception is when the same name has more then one file.
|
48
48
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
49
|
+
| | Epuber |
|
50
|
+
|-----------------|------------------------------------------------------------------------------------------------- |
|
51
|
+
|:book: | Creates seamless workflow for creating ebooks |
|
52
|
+
|:monorail: | Defines different _targets_ for several book stores (iBookstore, Google Play store, ...) |
|
53
|
+
|:wrench: | Extend and customise the functionality with _plugins_ |
|
54
|
+
|:pencil2: | Defines _constants_ so you can have link to related book in proper book store |
|
55
|
+
|:pencil: | Defines mechanisms to deal with duplicated |
|
56
|
+
|:tophat: | Easy setup assistant to get started in a few minutes |
|
57
|
+
|:ghost: | Automatically validates generated text so you don't have to worry about typos and other mistakes |
|
58
|
+
|:rocket: | Saves you **hours** when developing new or updating existing book |
|
59
|
+
|:page_with_curl: | Supports template engines, CSS preprocessors and standard EPUB formats at the same time |
|
60
|
+
|:computer: | Have local development web server to iterate and experiment quickly |
|
61
|
+
|:loop: | Automatically refresh web browser when some source file changes |
|
62
|
+
|:bomb: | Quick jumping through pages with arrow keys on keyboard |
|
63
|
+
|:closed_book: | Supports EPUB 2 and 3 |
|
64
64
|
|
65
65
|
|
66
66
|
## Installation
|
67
67
|
|
68
68
|
First of all Epuber uses [RMagick](https://github.com/rmagick/rmagick) which has several external dependencies, so you have to install that first:
|
69
69
|
|
70
|
-
- ImageMagick
|
70
|
+
- ImageMagick (only v6 for now)
|
71
71
|
- pkg-config
|
72
72
|
|
73
73
|
On OS X make sure you Xcode Command Line Tools installed:
|
74
74
|
|
75
75
|
xcode-select --install
|
76
76
|
|
77
|
-
And the easiest way to install prerequisites on
|
77
|
+
And the easiest way to install prerequisites on macOS is to use [brew](http://brew.sh):
|
78
78
|
|
79
|
-
brew install imagemagick pkg-config
|
79
|
+
brew install imagemagick@6 node@12 pkg-config
|
80
|
+
brew link --force imagemagick@6
|
81
|
+
brew link --force --overwrite node@12
|
80
82
|
|
81
83
|
On Ubuntu, you can run:
|
82
84
|
|
@@ -87,7 +89,7 @@ On Ubuntu, you can run:
|
|
87
89
|
|
88
90
|
Then just type following line to terminal:
|
89
91
|
|
90
|
-
sudo gem install epuber
|
92
|
+
[sudo] gem install epuber
|
91
93
|
|
92
94
|
If everything goes well, try running following line in terminal:
|
93
95
|
|
data/epuber.gemspec
CHANGED
@@ -14,14 +14,15 @@ Gem::Specification.new do |spec|
|
|
14
14
|
spec.summary = 'Epuber is simple tool to compile and pack source files into EPUB format.'
|
15
15
|
spec.homepage = Epuber::HOME_URL
|
16
16
|
spec.license = 'MIT'
|
17
|
-
spec.required_ruby_version = '>= 2.
|
17
|
+
spec.required_ruby_version = '>= 2.5'
|
18
18
|
|
19
19
|
spec.files = Dir['bin/**/*'] + Dir['lib/**/*'] + %w(epuber.gemspec Gemfile LICENSE.txt README.md)
|
20
20
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
21
21
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
22
22
|
spec.require_paths = ['lib']
|
23
23
|
|
24
|
-
spec.add_runtime_dependency 'activesupport', '~>
|
24
|
+
spec.add_runtime_dependency 'activesupport', '~> 6.0'
|
25
|
+
spec.add_runtime_dependency 'addressable', '~> 2.7'
|
25
26
|
spec.add_runtime_dependency 'nokogiri', '~> 1.8', '>= 1.8.2'
|
26
27
|
spec.add_runtime_dependency 'mime-types', '~> 3.0'
|
27
28
|
spec.add_runtime_dependency 'claide', '~> 1.0'
|
@@ -33,8 +34,8 @@ Gem::Specification.new do |spec|
|
|
33
34
|
spec.add_runtime_dependency 'sinatra-contrib', '~> 2.0'
|
34
35
|
spec.add_runtime_dependency 'thin', '~> 1.6'
|
35
36
|
|
36
|
-
spec.add_runtime_dependency 'rmagick', '~> 2
|
37
|
-
spec.add_runtime_dependency 'rubyzip', '~>
|
37
|
+
spec.add_runtime_dependency 'rmagick', '~> 4.2'
|
38
|
+
spec.add_runtime_dependency 'rubyzip', '~> 2.3'
|
38
39
|
|
39
40
|
spec.add_runtime_dependency 'epubcheck-ruby', '~> 4.0'
|
40
41
|
|
@@ -42,9 +43,9 @@ Gem::Specification.new do |spec|
|
|
42
43
|
spec.add_runtime_dependency 'coffee-script', '~> 2.4'
|
43
44
|
spec.add_runtime_dependency 'bade', '~> 0.2.4'
|
44
45
|
|
45
|
-
spec.add_development_dependency 'bundler'
|
46
|
+
spec.add_development_dependency 'bundler'
|
46
47
|
spec.add_development_dependency 'rspec', '~> 3.2'
|
47
|
-
spec.add_development_dependency 'rubocop', '~>
|
48
|
-
spec.add_development_dependency 'rake', '~>
|
49
|
-
spec.add_development_dependency 'fakefs', '~>
|
48
|
+
spec.add_development_dependency 'rubocop', '~> 1.14'
|
49
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
50
|
+
spec.add_development_dependency 'fakefs', '~> 1.3'
|
50
51
|
end
|
data/lib/epuber/book/target.rb
CHANGED
data/lib/epuber/book/toc_item.rb
CHANGED
@@ -4,85 +4,25 @@ require 'active_support/core_ext/string/access'
|
|
4
4
|
|
5
5
|
require_relative '../ruby_extensions/match_data'
|
6
6
|
require_relative '../checker'
|
7
|
-
|
7
|
+
require_relative '../compiler/problem'
|
8
8
|
|
9
9
|
module Epuber
|
10
10
|
class Checker
|
11
11
|
class TextChecker < Checker
|
12
|
-
class MatchProblem
|
12
|
+
class MatchProblem < Compiler::Problem
|
13
13
|
# @param message [String]
|
14
14
|
# @param file_path [String]
|
15
15
|
# @param match [MatchData]
|
16
16
|
#
|
17
17
|
def initialize(match, message, file_path)
|
18
|
-
|
19
|
-
@message = message
|
20
|
-
@file_path = file_path
|
21
|
-
end
|
22
|
-
|
23
|
-
# Formats caret symbol with space indent
|
24
|
-
#
|
25
|
-
# @param [Fixnum] indent
|
26
|
-
#
|
27
|
-
# @return [String]
|
28
|
-
#
|
29
|
-
def caret_symbol(indent)
|
30
|
-
' ' * indent + '^'
|
31
|
-
end
|
32
|
-
|
33
|
-
# Formats caret symbols for indent and length
|
34
|
-
#
|
35
|
-
# @param [Fixnum] length
|
36
|
-
# @param [Fixnum] indent
|
37
|
-
#
|
38
|
-
# @return [String]
|
39
|
-
#
|
40
|
-
def caret_symbols(indent, length)
|
41
|
-
start_sign = caret_symbol(indent)
|
42
|
-
end_sign = if length > 1
|
43
|
-
caret_symbol(length-2)
|
44
|
-
else
|
45
|
-
''
|
46
|
-
end
|
47
|
-
|
48
|
-
"#{start_sign}#{end_sign}"
|
49
|
-
end
|
50
|
-
|
51
|
-
def formatted_match_line
|
52
|
-
match_line = @match.matched_string
|
53
|
-
pre_line = @match.pre_match_lines.last || ''
|
54
|
-
|
55
|
-
pre = match_pre_line = pre_line
|
56
|
-
if remove_tabs(match_pre_line).length > 100
|
57
|
-
pre = "#{match_pre_line.first(20)}...#{match_pre_line.last(30)}"
|
58
|
-
end
|
59
|
-
|
60
|
-
pre = remove_tabs(pre)
|
61
|
-
|
62
|
-
post_line = @match.post_match_lines.first || ''
|
63
|
-
|
64
|
-
post = if post_line.length > 50
|
65
|
-
"#{post_line.first(50)}..."
|
66
|
-
else
|
67
|
-
post_line
|
68
|
-
end
|
69
|
-
|
70
|
-
[pre, match_line, post]
|
71
|
-
end
|
72
|
-
|
73
|
-
def remove_tabs(text)
|
74
|
-
text.gsub("\t", ' ' * 4)
|
75
|
-
end
|
76
|
-
|
77
|
-
def to_s
|
78
|
-
pre_original = @match.pre_match_lines.last || ''
|
79
|
-
pre, match_text, post = formatted_match_line
|
18
|
+
whole_text = match.pre_match + match.matched_string + match.post_match
|
80
19
|
|
81
|
-
|
20
|
+
line = match.pre_match_lines.count
|
21
|
+
column = (match.pre_match_lines.last || '').length + 1
|
22
|
+
length = match.matched_string.length
|
23
|
+
location = Epuber::Compiler::Problem::Location.new(line, column, length)
|
82
24
|
|
83
|
-
|
84
|
-
#{pre + match_text.ansi.red + post}
|
85
|
-
#{pointers}}
|
25
|
+
super(:warn, message, whole_text, location: location, file_path: file_path)
|
86
26
|
end
|
87
27
|
end
|
88
28
|
|
data/lib/epuber/command.rb
CHANGED
@@ -74,5 +74,14 @@ module Epuber
|
|
74
74
|
Epuber::Config.instance.save_lockfile
|
75
75
|
end
|
76
76
|
end
|
77
|
+
|
78
|
+
def pre_build_checks
|
79
|
+
Config.instance.warn_for_outdated_versions!
|
80
|
+
|
81
|
+
if !Config.instance.same_version_as_last_run? && File.exist?(Config.instance.working_path)
|
82
|
+
UI.warning('Using different version of Epuber or Bade, removing all build caches')
|
83
|
+
Config.instance.remove_build_caches
|
84
|
+
end
|
85
|
+
end
|
77
86
|
end
|
78
87
|
end
|
data/lib/epuber/command/build.rb
CHANGED
@@ -68,11 +68,11 @@ module Epuber
|
|
68
68
|
xhtml_content
|
69
69
|
end
|
70
70
|
|
71
|
-
# @param [Array] errors
|
71
|
+
# @param [Array<Epuber::Compiler::Problem>] errors
|
72
72
|
#
|
73
73
|
def process_nokogiri_errors(errors)
|
74
|
-
errors.each do |
|
75
|
-
UI.warning(
|
74
|
+
errors.each do |problem|
|
75
|
+
UI.warning(problem, location: self)
|
76
76
|
end
|
77
77
|
end
|
78
78
|
|
@@ -86,25 +86,18 @@ module Epuber
|
|
86
86
|
book = compilation_context.book
|
87
87
|
file_resolver = compilation_context.file_resolver
|
88
88
|
|
89
|
-
xhtml_doc = UI.print_step_processing_time('parsing XHTML file') do
|
90
|
-
XHTMLProcessor.
|
89
|
+
xhtml_doc, errors = UI.print_step_processing_time('parsing XHTML file') do
|
90
|
+
XHTMLProcessor.xml_doc_from_str_with_errors(content, source_path)
|
91
91
|
end
|
92
92
|
|
93
93
|
if compilation_context.release_build && xhtml_doc.errors.count > 0
|
94
|
-
process_nokogiri_errors(
|
94
|
+
process_nokogiri_errors(errors)
|
95
95
|
end
|
96
96
|
|
97
97
|
UI.print_step_processing_time('adding missing elements') do
|
98
98
|
XHTMLProcessor.add_missing_root_elements(xhtml_doc, book.title, target.epub_version)
|
99
99
|
end
|
100
100
|
|
101
|
-
UI.print_step_processing_time('adding default things') do
|
102
|
-
XHTMLProcessor.add_styles(xhtml_doc, default_styles(target, file_resolver))
|
103
|
-
XHTMLProcessor.add_scripts(xhtml_doc, default_scripts(target, file_resolver))
|
104
|
-
|
105
|
-
XHTMLProcessor.add_viewport(xhtml_doc, target.default_viewport) unless target.default_viewport.nil?
|
106
|
-
end
|
107
|
-
|
108
101
|
# resolve links to files, add other linked resources and compute correct path
|
109
102
|
UI.print_step_processing_time('resolving links') do
|
110
103
|
XHTMLProcessor.resolve_links(xhtml_doc, destination_path, file_resolver.dest_finder)
|
@@ -119,6 +112,13 @@ module Epuber
|
|
119
112
|
XHTMLProcessor.resolve_stylesheets(xhtml_doc, destination_path, file_resolver)
|
120
113
|
end
|
121
114
|
|
115
|
+
UI.print_step_processing_time('adding default things') do
|
116
|
+
XHTMLProcessor.add_styles(xhtml_doc, default_styles(target, file_resolver))
|
117
|
+
XHTMLProcessor.add_scripts(xhtml_doc, default_scripts(target, file_resolver))
|
118
|
+
|
119
|
+
XHTMLProcessor.add_viewport(xhtml_doc, target.default_viewport) unless target.default_viewport.nil?
|
120
|
+
end
|
121
|
+
|
122
122
|
XHTMLProcessor.resolve_mathml_namespace(xhtml_doc)
|
123
123
|
|
124
124
|
UI.print_step_processing_time('investigating properties') do
|
@@ -121,7 +121,7 @@ module Epuber
|
|
121
121
|
end
|
122
122
|
end
|
123
123
|
|
124
|
-
if @target.epub_version >= 3 && toc_items.length > 0
|
124
|
+
if @target.epub_version >= 3 && toc_items.length > 0 && contains_item_with_title(toc_items)
|
125
125
|
@xml.ol do
|
126
126
|
iterate_lambda.call
|
127
127
|
end
|
@@ -130,6 +130,10 @@ module Epuber
|
|
130
130
|
end
|
131
131
|
end
|
132
132
|
|
133
|
+
def contains_item_with_title(toc_items)
|
134
|
+
toc_items.any? { |a| a.title || contains_item_with_title(a.sub_items) }
|
135
|
+
end
|
136
|
+
|
133
137
|
# @param toc_item [Epuber::Book::TocItem]
|
134
138
|
#
|
135
139
|
def visit_toc_item(toc_item)
|
@@ -257,18 +257,30 @@ module Epuber
|
|
257
257
|
# @return [String]
|
258
258
|
#
|
259
259
|
def create_id_from_path(path)
|
260
|
-
|
260
|
+
return nil if path.nil?
|
261
|
+
|
262
|
+
path.gsub('/', '.').gsub(/^[0-9]*/, '')
|
261
263
|
end
|
262
264
|
|
263
265
|
# Creates proper mime-type for file
|
264
266
|
#
|
265
|
-
# @param file [Epuber::
|
267
|
+
# @param file [Epuber::Compiler::FileTypes::AbstractFile | String]
|
266
268
|
#
|
267
269
|
# @return [String]
|
268
270
|
#
|
269
271
|
def mime_type_for(file)
|
270
|
-
filename = file.
|
271
|
-
|
272
|
+
filename = if file.is_a?(String)
|
273
|
+
file
|
274
|
+
else
|
275
|
+
file.destination_path
|
276
|
+
end
|
277
|
+
|
278
|
+
case File.extname(filename)
|
279
|
+
when '.ttf', '.otf'
|
280
|
+
'application/vnd.ms-opentype'
|
281
|
+
else
|
282
|
+
MIME::Types.of(filename).first.content_type
|
283
|
+
end
|
272
284
|
end
|
273
285
|
|
274
286
|
# Creates hash of namespaces for root package element
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
|
4
|
+
module Epuber
|
5
|
+
class Compiler
|
6
|
+
class Problem
|
7
|
+
class Location
|
8
|
+
attr_reader :line
|
9
|
+
attr_reader :column
|
10
|
+
attr_reader :length
|
11
|
+
|
12
|
+
def initialize(line, column, length = nil)
|
13
|
+
@line = line
|
14
|
+
@column = column
|
15
|
+
@length = length || 1
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :level
|
20
|
+
attr_reader :message
|
21
|
+
attr_reader :source
|
22
|
+
attr_reader :location
|
23
|
+
attr_reader :file_path
|
24
|
+
|
25
|
+
def initialize(level, message, source, location: nil, line: nil, column: nil, length: nil, file_path: nil)
|
26
|
+
@level = level
|
27
|
+
@message = message
|
28
|
+
@source = source
|
29
|
+
@location = location
|
30
|
+
if @location.nil? && line && column
|
31
|
+
@location = Location.new(line, column, length)
|
32
|
+
end
|
33
|
+
|
34
|
+
@file_path = file_path
|
35
|
+
end
|
36
|
+
|
37
|
+
# Formats caret symbol with space indent
|
38
|
+
#
|
39
|
+
# @param [Fixnum] indent
|
40
|
+
#
|
41
|
+
# @return [String]
|
42
|
+
#
|
43
|
+
def self.caret_symbol(indent)
|
44
|
+
' ' * indent + '^'
|
45
|
+
end
|
46
|
+
|
47
|
+
# Formats caret symbols for indent and length
|
48
|
+
#
|
49
|
+
# @param [Fixnum] length
|
50
|
+
# @param [Fixnum] indent
|
51
|
+
#
|
52
|
+
# @return [String]
|
53
|
+
#
|
54
|
+
def self.caret_symbols(indent, length)
|
55
|
+
start_sign = caret_symbol(indent)
|
56
|
+
end_sign = if length > 1
|
57
|
+
caret_symbol(length-2)
|
58
|
+
else
|
59
|
+
''
|
60
|
+
end
|
61
|
+
|
62
|
+
"#{start_sign}#{end_sign}"
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.remove_tabs(text)
|
66
|
+
text.gsub("\t", ' ' * 4)
|
67
|
+
end
|
68
|
+
|
69
|
+
# @param [Location] location
|
70
|
+
#
|
71
|
+
def self.text_at(text, location)
|
72
|
+
line_index = location.line - 1
|
73
|
+
column_index = location.column - 1
|
74
|
+
|
75
|
+
lines = text.split("\n")
|
76
|
+
|
77
|
+
line = lines[line_index] || ''
|
78
|
+
matched_text = line[column_index ... column_index + location.length] || ''
|
79
|
+
|
80
|
+
pre = (lines[0 ... line_index] + [line[0 ... column_index]]).join("\n")
|
81
|
+
post = ([line[column_index + location.length .. line.length]] + (lines[location.line .. lines.count] || [])).join("\n")
|
82
|
+
|
83
|
+
[pre, matched_text, post]
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.formatted_match_line(text, location)
|
87
|
+
pre, matched, post = text_at(text, location)
|
88
|
+
|
89
|
+
pre_line = pre.split("\n").last || ''
|
90
|
+
post_line = post.split("\n").first || ''
|
91
|
+
|
92
|
+
pre = match_pre_line = pre_line
|
93
|
+
if remove_tabs(match_pre_line).length > 100
|
94
|
+
pre = "#{match_pre_line.first(20)}...#{match_pre_line.last(30)}"
|
95
|
+
end
|
96
|
+
|
97
|
+
pre = remove_tabs(pre)
|
98
|
+
|
99
|
+
post = if post_line.length > 50
|
100
|
+
"#{post_line.first(50)}..."
|
101
|
+
else
|
102
|
+
post_line
|
103
|
+
end
|
104
|
+
|
105
|
+
[pre, matched, post]
|
106
|
+
end
|
107
|
+
|
108
|
+
def to_s
|
109
|
+
pre, match_text, post = self.class.formatted_match_line(@source, @location)
|
110
|
+
|
111
|
+
pointers = self.class.caret_symbols(pre.length, @location.length)
|
112
|
+
colored_match_text = match_text.empty? ? match_text : match_text.ansi.red
|
113
|
+
column = @location.column
|
114
|
+
line = [@location.line, 1].max
|
115
|
+
|
116
|
+
[
|
117
|
+
"#{@file_path}:#{line} column: #{column} --- #{@message}",
|
118
|
+
' ' + pre + colored_match_text + post,
|
119
|
+
' ' + pointers,
|
120
|
+
].join("\n")
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
require 'nokogiri'
|
4
|
-
require '
|
4
|
+
require 'addressable'
|
5
5
|
|
6
6
|
|
7
7
|
module Epuber
|
@@ -19,14 +19,14 @@ module Epuber
|
|
19
19
|
#
|
20
20
|
# @return [Nokogiri::XML::Document] parsed document
|
21
21
|
#
|
22
|
-
def self.
|
22
|
+
def self.xml_doc_from_str_with_errors(text, file_path = nil)
|
23
23
|
if /\A[\n\r ]+(<\?xml)/ =~ text
|
24
24
|
UI.warning('XML header must be at the beginning of document', location: UI::Location.new(file_path, 1))
|
25
25
|
|
26
26
|
text = text.lstrip
|
27
27
|
end
|
28
28
|
|
29
|
-
xml_header =
|
29
|
+
xml_header = nil
|
30
30
|
if /\A\s*(<\?xml[^>]*\?>)/ =~ text
|
31
31
|
match = Regexp.last_match
|
32
32
|
xml_header = text[match.begin(1)...match.end(1)]
|
@@ -50,20 +50,38 @@ module Epuber
|
|
50
50
|
Nokogiri::XML::ParseOptions::NOERROR | # to silence any errors or warnings printing into console
|
51
51
|
Nokogiri::XML::ParseOptions::NOWARNING
|
52
52
|
|
53
|
-
doc = Nokogiri::XML("#{before}<root>#{text}</root>",
|
53
|
+
doc = Nokogiri::XML("#{before}<root>#{text}</root>", file_path, nil, parse_options)
|
54
|
+
text_for_errors = before + text
|
54
55
|
doc.encoding = 'UTF-8'
|
55
56
|
doc.file_path = file_path
|
56
57
|
|
58
|
+
if doc.errors.empty?
|
59
|
+
errors = []
|
60
|
+
else
|
61
|
+
errors = doc.errors.map do |e|
|
62
|
+
Problem.new(:error, e.message, text_for_errors, line: e.line, column: e.column, file_path: file_path)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
57
66
|
root = root_node = doc.root
|
58
67
|
root_elements = root.children.select { |a| a.element? || a.comment? }
|
59
68
|
|
60
69
|
if root_elements.count == 1
|
61
70
|
doc.root = root_elements.first
|
71
|
+
elsif root_node.at_css('html')
|
72
|
+
doc.root = root_node.at_css('html')
|
62
73
|
elsif root_node.at_css('body').nil?
|
63
74
|
root_node.node_name = 'body'
|
75
|
+
else
|
76
|
+
root_node.node_name = 'html'
|
64
77
|
end
|
65
78
|
|
66
|
-
doc
|
79
|
+
[doc, errors]
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.xml_document_from_string(text, file_path = nil)
|
83
|
+
xml, errros = self.xml_doc_from_str_with_errors(text, file_path)
|
84
|
+
xml
|
67
85
|
end
|
68
86
|
|
69
87
|
|
@@ -83,25 +101,37 @@ module Epuber
|
|
83
101
|
def self.add_missing_root_elements(xhtml_doc, title, epub_version)
|
84
102
|
# add missing body element
|
85
103
|
if xhtml_doc.at_css('body').nil?
|
86
|
-
xhtml_doc.root.
|
104
|
+
if xhtml_doc.root.node_name == 'html'
|
105
|
+
xhtml_doc.root << xhtml_doc.create_element('body')
|
106
|
+
else
|
107
|
+
xhtml_doc.root.surround_with_element('body')
|
108
|
+
end
|
87
109
|
end
|
88
110
|
|
111
|
+
html = xhtml_doc.at_css('html')
|
112
|
+
|
89
113
|
# add missing root html element
|
90
|
-
if
|
114
|
+
if html.nil?
|
91
115
|
attrs = {}
|
92
116
|
attrs['xmlns'] = 'http://www.w3.org/1999/xhtml'
|
93
117
|
attrs['xmlns:epub'] = 'http://www.idpf.org/2007/ops' if epub_version >= 3
|
94
|
-
xhtml_doc.root.surround_with_element('html', attrs)
|
118
|
+
html = xhtml_doc.root.surround_with_element('html', attrs)
|
119
|
+
elsif html.namespaces.empty?
|
120
|
+
html['xmlns'] = 'http://www.w3.org/1999/xhtml'
|
121
|
+
html['xmlns:epub'] = 'http://www.idpf.org/2007/ops' if epub_version >= 3
|
95
122
|
end
|
96
123
|
|
97
124
|
# add missing head in html
|
98
125
|
if xhtml_doc.at_css('html > head').nil?
|
99
|
-
html = xhtml_doc.css('html').first
|
100
126
|
head = xhtml_doc.create_element('head')
|
101
127
|
head << xhtml_doc.create_element('title', title)
|
102
128
|
head << xhtml_doc.create_element('meta', charset: 'utf-8') if epub_version >= 3.0
|
103
129
|
|
104
|
-
html.children.first
|
130
|
+
if (first = html.children.first)
|
131
|
+
first.before(head)
|
132
|
+
else
|
133
|
+
html << head
|
134
|
+
end
|
105
135
|
end
|
106
136
|
|
107
137
|
# https://github.com/IDPF/epubcheck/issues/631
|
@@ -178,7 +208,7 @@ module Epuber
|
|
178
208
|
uri = URI(path)
|
179
209
|
rescue URI::InvalidURIError
|
180
210
|
begin
|
181
|
-
uri = URI(URI
|
211
|
+
uri = URI(Addressable::URI.encode(path))
|
182
212
|
rescue URI::InvalidURIError
|
183
213
|
# skip not valid uri
|
184
214
|
raise UnparseableLinkError, "Unparseable link `#{path}`"
|
data/lib/epuber/config.rb
CHANGED
@@ -123,14 +123,26 @@ module Epuber
|
|
123
123
|
|
124
124
|
def warn_for_outdated_versions!
|
125
125
|
if bookspec_lockfile.epuber_version > Epuber::VERSION
|
126
|
-
UI.warning('Warning: the running version of Epuber is older than the version that created the lockfile. We suggest you upgrade to the latest version of Epuber by running `gem install epuber
|
126
|
+
UI.warning('Warning: the running version of Epuber is older than the version that created the lockfile. We suggest you upgrade to the latest version of Epuber by running `gem install epuber`.')
|
127
127
|
end
|
128
128
|
|
129
129
|
if bookspec_lockfile.bade_version && bookspec_lockfile.bade_version > Bade::VERSION
|
130
|
-
UI.warning('Warning: the running version of Bade is older than the version that created the lockfile. We suggest you upgrade to the latest version of Bade by running `gem install bade
|
130
|
+
UI.warning('Warning: the running version of Bade is older than the version that created the lockfile. We suggest you upgrade to the latest version of Bade by running `gem install bade`.')
|
131
131
|
end
|
132
132
|
end
|
133
133
|
|
134
|
+
def same_version_as_last_run?
|
135
|
+
!(bookspec_lockfile.epuber_version != Epuber::VERSION ||
|
136
|
+
bookspec_lockfile.bade_version.nil? ||
|
137
|
+
bookspec_lockfile.bade_version != Bade::VERSION)
|
138
|
+
end
|
139
|
+
|
140
|
+
def remove_build_caches
|
141
|
+
FileUtils.rm_rf(File.join(working_path, 'build_cache'))
|
142
|
+
FileUtils.rm_rf(File.join(working_path, 'build'))
|
143
|
+
FileUtils.rm_rf(File.join(working_path, 'metadata'))
|
144
|
+
end
|
145
|
+
|
134
146
|
# ---------------------------------------------------------------------------------------------------------------- #
|
135
147
|
|
136
148
|
class << self
|
data/lib/epuber/dsl/attribute.rb
CHANGED
@@ -47,8 +47,10 @@ module Epuber
|
|
47
47
|
@auto_convert = auto_convert
|
48
48
|
@types = if !types.nil?
|
49
49
|
types
|
50
|
-
elsif @default_value
|
50
|
+
elsif @default_value && @auto_convert.empty?
|
51
51
|
[@default_value.class]
|
52
|
+
elsif !@auto_convert.empty?
|
53
|
+
[@auto_convert.values.first]
|
52
54
|
else
|
53
55
|
[String]
|
54
56
|
end
|
@@ -234,6 +236,8 @@ module Epuber
|
|
234
236
|
return dest_class.call(value)
|
235
237
|
elsif dest_class.respond_to?(:parse)
|
236
238
|
return dest_class.parse(value)
|
239
|
+
elsif dest_class <= String
|
240
|
+
return value.to_s
|
237
241
|
elsif dest_class.respond_to?(:new)
|
238
242
|
return dest_class.new(value)
|
239
243
|
else
|
@@ -66,12 +66,8 @@ module Epuber
|
|
66
66
|
parent.send(key)
|
67
67
|
|
68
68
|
elsif !attr.default_value.nil?
|
69
|
-
#
|
70
|
-
|
71
|
-
root_obj.send(attr.writer_name, attr.default_value)
|
72
|
-
|
73
|
-
# and return the default value
|
74
|
-
attr.default_value
|
69
|
+
# just return the default value
|
70
|
+
attr.converted_value(attr.default_value)
|
75
71
|
end
|
76
72
|
end
|
77
73
|
|
data/lib/epuber/dsl/object.rb
CHANGED
@@ -71,7 +71,7 @@ mixin iterate_spine(toc_item)
|
|
71
71
|
|
72
72
|
|
73
73
|
+section('Targets')
|
74
|
-
- book.
|
74
|
+
- book.buildable_targets.each do |b_target|
|
75
75
|
p(class: 'target_selected' if target == b_target): a(href: "/change_target/#{b_target.name}")= b_target.name
|
76
76
|
- end
|
77
77
|
|
@@ -191,7 +191,13 @@ module Epuber
|
|
191
191
|
|
192
192
|
comps = []
|
193
193
|
comps << message.to_s
|
194
|
-
|
194
|
+
if !location.nil? && !(message.is_a?(Epuber::Compiler::Problem) || message.is_a?(Epuber::Checker::TextChecker::MatchProblem))
|
195
|
+
if location.lineno
|
196
|
+
comps << " (in file #{location.path} line #{location.lineno})"
|
197
|
+
else
|
198
|
+
comps << " (in file #{location.path})"
|
199
|
+
end
|
200
|
+
end
|
195
201
|
|
196
202
|
comps.join("\n").ansi.send(_color_from_level(level))
|
197
203
|
end
|
data/lib/epuber/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: epuber
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Roman Kříž
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-05-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -16,14 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '6.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '6.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: addressable
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.7'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.7'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: nokogiri
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -162,28 +176,28 @@ dependencies:
|
|
162
176
|
requirements:
|
163
177
|
- - "~>"
|
164
178
|
- !ruby/object:Gem::Version
|
165
|
-
version: '2
|
179
|
+
version: '4.2'
|
166
180
|
type: :runtime
|
167
181
|
prerelease: false
|
168
182
|
version_requirements: !ruby/object:Gem::Requirement
|
169
183
|
requirements:
|
170
184
|
- - "~>"
|
171
185
|
- !ruby/object:Gem::Version
|
172
|
-
version: '2
|
186
|
+
version: '4.2'
|
173
187
|
- !ruby/object:Gem::Dependency
|
174
188
|
name: rubyzip
|
175
189
|
requirement: !ruby/object:Gem::Requirement
|
176
190
|
requirements:
|
177
191
|
- - "~>"
|
178
192
|
- !ruby/object:Gem::Version
|
179
|
-
version: '
|
193
|
+
version: '2.3'
|
180
194
|
type: :runtime
|
181
195
|
prerelease: false
|
182
196
|
version_requirements: !ruby/object:Gem::Requirement
|
183
197
|
requirements:
|
184
198
|
- - "~>"
|
185
199
|
- !ruby/object:Gem::Version
|
186
|
-
version: '
|
200
|
+
version: '2.3'
|
187
201
|
- !ruby/object:Gem::Dependency
|
188
202
|
name: epubcheck-ruby
|
189
203
|
requirement: !ruby/object:Gem::Requirement
|
@@ -250,16 +264,16 @@ dependencies:
|
|
250
264
|
name: bundler
|
251
265
|
requirement: !ruby/object:Gem::Requirement
|
252
266
|
requirements:
|
253
|
-
- - "
|
267
|
+
- - ">="
|
254
268
|
- !ruby/object:Gem::Version
|
255
|
-
version: '
|
269
|
+
version: '0'
|
256
270
|
type: :development
|
257
271
|
prerelease: false
|
258
272
|
version_requirements: !ruby/object:Gem::Requirement
|
259
273
|
requirements:
|
260
|
-
- - "
|
274
|
+
- - ">="
|
261
275
|
- !ruby/object:Gem::Version
|
262
|
-
version: '
|
276
|
+
version: '0'
|
263
277
|
- !ruby/object:Gem::Dependency
|
264
278
|
name: rspec
|
265
279
|
requirement: !ruby/object:Gem::Requirement
|
@@ -280,43 +294,43 @@ dependencies:
|
|
280
294
|
requirements:
|
281
295
|
- - "~>"
|
282
296
|
- !ruby/object:Gem::Version
|
283
|
-
version: '
|
297
|
+
version: '1.14'
|
284
298
|
type: :development
|
285
299
|
prerelease: false
|
286
300
|
version_requirements: !ruby/object:Gem::Requirement
|
287
301
|
requirements:
|
288
302
|
- - "~>"
|
289
303
|
- !ruby/object:Gem::Version
|
290
|
-
version: '
|
304
|
+
version: '1.14'
|
291
305
|
- !ruby/object:Gem::Dependency
|
292
306
|
name: rake
|
293
307
|
requirement: !ruby/object:Gem::Requirement
|
294
308
|
requirements:
|
295
309
|
- - "~>"
|
296
310
|
- !ruby/object:Gem::Version
|
297
|
-
version: '
|
311
|
+
version: '13.0'
|
298
312
|
type: :development
|
299
313
|
prerelease: false
|
300
314
|
version_requirements: !ruby/object:Gem::Requirement
|
301
315
|
requirements:
|
302
316
|
- - "~>"
|
303
317
|
- !ruby/object:Gem::Version
|
304
|
-
version: '
|
318
|
+
version: '13.0'
|
305
319
|
- !ruby/object:Gem::Dependency
|
306
320
|
name: fakefs
|
307
321
|
requirement: !ruby/object:Gem::Requirement
|
308
322
|
requirements:
|
309
323
|
- - "~>"
|
310
324
|
- !ruby/object:Gem::Version
|
311
|
-
version: '
|
325
|
+
version: '1.3'
|
312
326
|
type: :development
|
313
327
|
prerelease: false
|
314
328
|
version_requirements: !ruby/object:Gem::Requirement
|
315
329
|
requirements:
|
316
330
|
- - "~>"
|
317
331
|
- !ruby/object:Gem::Version
|
318
|
-
version: '
|
319
|
-
description:
|
332
|
+
version: '1.3'
|
333
|
+
description:
|
320
334
|
email:
|
321
335
|
- samnung@gmail.com
|
322
336
|
executables:
|
@@ -369,6 +383,7 @@ files:
|
|
369
383
|
- lib/epuber/compiler/meta_inf_generator.rb
|
370
384
|
- lib/epuber/compiler/nav_generator.rb
|
371
385
|
- lib/epuber/compiler/opf_generator.rb
|
386
|
+
- lib/epuber/compiler/problem.rb
|
372
387
|
- lib/epuber/compiler/xhtml_processor.rb
|
373
388
|
- lib/epuber/config.rb
|
374
389
|
- lib/epuber/dsl/attribute.rb
|
@@ -379,7 +394,6 @@ files:
|
|
379
394
|
- lib/epuber/lockfile.rb
|
380
395
|
- lib/epuber/plugin.rb
|
381
396
|
- lib/epuber/ruby_extensions/match_data.rb
|
382
|
-
- lib/epuber/ruby_extensions/string.rb
|
383
397
|
- lib/epuber/ruby_extensions/thread.rb
|
384
398
|
- lib/epuber/server.rb
|
385
399
|
- lib/epuber/server/auto_refresh/auto_refresh.coffee
|
@@ -538,7 +552,7 @@ homepage: https://github.com/epuber-io/epuber
|
|
538
552
|
licenses:
|
539
553
|
- MIT
|
540
554
|
metadata: {}
|
541
|
-
post_install_message:
|
555
|
+
post_install_message:
|
542
556
|
rdoc_options: []
|
543
557
|
require_paths:
|
544
558
|
- lib
|
@@ -546,16 +560,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
546
560
|
requirements:
|
547
561
|
- - ">="
|
548
562
|
- !ruby/object:Gem::Version
|
549
|
-
version: '2.
|
563
|
+
version: '2.5'
|
550
564
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
551
565
|
requirements:
|
552
566
|
- - ">="
|
553
567
|
- !ruby/object:Gem::Version
|
554
568
|
version: '0'
|
555
569
|
requirements: []
|
556
|
-
|
557
|
-
|
558
|
-
signing_key:
|
570
|
+
rubygems_version: 3.2.17
|
571
|
+
signing_key:
|
559
572
|
specification_version: 4
|
560
573
|
summary: Epuber is simple tool to compile and pack source files into EPUB format.
|
561
574
|
test_files: []
|
@@ -1,16 +0,0 @@
|
|
1
|
-
|
2
|
-
begin
|
3
|
-
require 'unicode_normalize'
|
4
|
-
rescue LoadError
|
5
|
-
# silently fail
|
6
|
-
|
7
|
-
require 'active_support/multibyte/unicode'
|
8
|
-
|
9
|
-
class String
|
10
|
-
unless respond_to?(:unicode_normalize)
|
11
|
-
def unicode_normalize
|
12
|
-
ActiveSupport::Multibyte::Unicode.normalize(self)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|