Skip to content

Instantly share code, notes, and snippets.

@KitaitiMakoto
Created January 2, 2016 09:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save KitaitiMakoto/7b2286b61a0bafcc5926 to your computer and use it in GitHub Desktop.
Save KitaitiMakoto/7b2286b61a0bafcc5926 to your computer and use it in GitHub Desktop.
# coding: utf-8
gem 'epub-parser', '>= 0.2.4'
gem 'epub-maker', '>= 0.0.2'
require 'English'
require 'epub/maker'
require 'epub/parser/cfi'
require 'nokogiri/xml/range'
TARGET_RELEASE_IDENTIFIER = 'urn:uuid:14eadcd9-214a-4f45-9623-d8ac54f22af7@2015-12-04T09:00:00Z'
ERRATA = [
{target: '/6/36!/4/2/16/5,:25,:27', operation: :replace, replace: 'ri'},
{target: '/6/36!/4/2/18/7,:33,:35', operation: :replace, replace: 'ri'},
{target: '/6/46!/4/2/70/6/1,:0,:4', operation: :replace, replace: 'send'},
{target: '/6/52!/4/2/28/1,:61,:62', operation: :remove},
{target: '/6/68!/4/2/44/1:267', operation: :add, add: 'が'},
{target: '/6/98!/4/2/12/9:7', operation: :add, add: '*10'},
{target: '/6/118!/4/2/50/3,:45,:46', operation: :remove}
]
UNOFFICIAL_ERRATA = [
{target: '/6/84!/4/2/34/4/1,:53,:54', operation: :remove}
]
NEW_MODIFIED = '2015-12-30T14:03:00Z'
def main(argv)
epub_path = argv.shift
if epub_path.nil? or !File.file?(epub_path)
$stderr.puts "EPUBファイルを指定してください。"
$stderr.puts usage
abort
end
epub = EPUB::Parser.parse(epub_path)
$stderr.puts "#{$PROGRAM_NAME}: このプログラムは指定された本(#{epub_path})を上書きします。よろしいですか?(y/N)"
unless gets.chomp.downcase == "y"
exit
end
unless assert_release_identifier(epub)
$stderr.puts "指定された本は、エラッタが適用可能なバージョン(Release Identifier)ではありません。"
$stderr.puts " エラッタの対象バージョン:#{TARGET_RELEASE_IDENTIFIER}"
$stderr.puts " 指定された本のバージョン:#{epub.release_identifier}"
abort
end
reflect_errata(epub)
update_modified(epub)
rescue => error
$stderr.puts error
$stderr.puts error.backtrace
$stderr.puts usage
abort
end
def usage
"Usage: ruby #{$PROGRAM_NAME} EPUB_PATH"
end
def assert_release_identifier(epub)
epub.release_identifier == TARGET_RELEASE_IDENTIFIER
end
def reflect_errata(epub)
errata = setup_errata
errata.each do |erratum|
reflect_erratum erratum, epub
end
end
def update_modified(epub)
epub.package.edit do |package|
package.metadata.modified.content = NEW_MODIFIED
end
end
def reflect_erratum(erratum, epub)
if erratum[:target].kind_of? EPUB::CFI::Location
item, start_node = end_node = get_item_and_element(erratum[:target], epub)
start_offset = end_offset = erratum[:target].paths.last.offset.value
else
# TODO: Search nodes from parent subpath
item, start_node = get_item_and_element(erratum[:target].first, epub)
_, end_node = get_item_and_element(erratum[:target].last, epub)
start_offset = erratum[:target].first.paths.last.offset.value
end_offset = erratum[:target].last.paths.last.offset.value
end
range = Nokogiri::XML::Range.new(start_node, start_offset, end_node, end_offset)
# page_title = (range.document/'h1').first.content.gsub(/\s+/, ' ')
# puts '==========================='
# puts "#{page_title}(#{item.href})"
# puts '-----------BEFORE----------'
# puts range.start_node.parent.content
case erratum[:operation]
when :add
range.insert_node Nokogiri::XML::Text.new(erratum[:add], range.document)
when :remove
range.delete_contents
when :replace
range.delete_contents
range.insert_node Nokogiri::XML::Text.new(erratum[:replace], range.document)
end
# puts '-----------AFTER----------'
# puts range.start_node.parent.content
item.content = range.document.to_xml
item.save
end
def get_item_and_element(cfi, epub)
path_in_package = cfi.paths.first
step_to_itemref = path_in_package.steps[1]
itemref = epub.spine.itemrefs[step_to_itemref.step / 2 - 1]
doc = itemref.item.content_document.nokogiri
path_in_doc = cfi.paths[1]
current_node = doc.root
path_in_doc.steps.each do |step|
if step.element?
current_node = current_node.element_children[step.value / 2 - 1]
else
element_index = (step.value - 1) / 2 - 1
if element_index == -1
current_node = current_node.children.first
else
prev = current_node.element_children[element_index]
break unless prev
current_node = prev.next_sibling
break unless current_node
end
end
end
[itemref.item, current_node]
end
def setup_errata
(ERRATA + UNOFFICIAL_ERRATA).map {|erratum|
erratum[:target] = EPUB::CFI(erratum[:target])
erratum
}.sort_by {|erratum|
cfi = erratum[:target]
cfi = cfi.first if cfi.kind_of? EPUB::CFI::Range
cfi
}.reverse
end
main(ARGV)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment