目次
はじめに
マンガのEPUBのリーダーアプリとしてNeeViewを使っている。 これはEPUBがzipファイルであることを利用して、画像だけのEPUBであれば簡易的にEPUBリーダーになる。
ところが、これは画像ファイル名が連番であることを暗に前提としている。というのも、連番になっていないEPUBではページの順番が狂うのだ。
ということで、EPUBの画像ファイル名を連番に変換するrubyスクリプトを書いた。あまり綺麗でないが、まあ使えるだろう。
(2025-08-05)epub-parserを使って、書き直した。中身は大分すっきりしたと思う。 注意としては、EPUB 3に準拠できていないEPUBではエラーになる。まずはEPUB 3に準拠させてから実行する必要がある。 EPUBの検証にはEPUB-Checkerが使える。
使い方
- EPUBファイルを a.zipに名前を変更し、フォルダ名 a に展開する。
- このスクリプトを実行する。
epub-img-files-renum.rb a
- a より上位のフォルダでMakefileを実行してEPUBを生成する。
EPUB のフォルダ構成
EPUB のフォルダ構成はたいて以下のような三つのパターンになっている:
パターンA
a/ ├── META-INF/ ├── OPS/ ├── js/ ├── mimetype └── rights.xml
パターンB
a/ ├── mimetype ├── META-INF/ └── OEBPS/
パターンC
a/ ├── mimetype ├── META-INF/ └── item/ └── js/ └── rights.xml
Ruby スクリプト
例えば epub-img-files-renum.rbという名前で保存する。
#!/usr/bin/env ruby
require 'epub/parser'
require 'nokogiri'
require 'pathname'
require 'find'
dry_run = false
ARGV.each{|input|
if FileTest.directory?(input)
dirName = input
end
book = EPUB::Parser.parse(dirName)
img_OldNew = Hash.new
cnt = 0
book.spine.itemrefs.each{|itemref|
item = itemref.item
media_type = item.media_type
if media_type.start_with?('image/')
fileNameOld = item.href
filePathOld = Pathname(File.basename(fileNameOld))
ext = File.extname(fileNameOld)
fileNameNew = sprintf("image_%06d#{ext}", cnt)
filePathNew = Pathname(fileNameNew)
img_OldNew[filePathOld.to_s] = filePathNew.to_s
cnt += 1
elsif
content = item.read
doc = Nokogiri::XML(content)
if doc.css('img').empty?
ns = {
'svg' => 'http://www.w3.org/2000/svg',
'xlink' => 'http://www.w3.org/1999/xlink'
}
image_nodes = doc.xpath('//svg:image', ns)
imgs = image_nodes.map { |node| node.attribute_with_ns('href', ns['xlink'])&.value }
else
imgs = []
doc.css('img').each{|img|
imgs.push(img['src'])
}
end
imgs.each{|imgLink|
# p imgLink
fileNameOld = imgLink
filePathOld = Pathname(File.basename(fileNameOld))
ext = File.extname(fileNameOld)
fileNameNew = sprintf("image_%06d#{ext}", cnt)
filePathNew = Pathname(fileNameNew)
img_OldNew[filePathOld.to_s] = filePathNew.to_s
cnt += 1
}
end
}
p img_OldNew
imgFileList = Array.new
xmlFileList = Array.new
pathDir = Pathname(dirName)
Find.find(pathDir) {|f|
if /\.(png|jpg|jpeg|gif)/ =~ f
if !FileTest.directory?(f)
imgFileList.push(f)
end
elsif /mimetype/ !~ f
if !FileTest.directory?(f)
xmlFileList.push(f)
end
end
}
p imgFileList
p xmlFileList
# xmlの中身の書き換え
xmlFileList.each{|xml|
xml_new = ""
xml_old = ""
File.open(xml){|f|
xml_new = f.read
xml_old = xml_new.dup
img_OldNew.each_key{|key|
if /(#{key})/ =~ xml_new
#p $1
#p img_OldNew[key]
xml_new = xml_new.gsub(/#{key}/, img_OldNew[key])
end
}
}
if !dry_run
if xml_new != xml_old
File.open(xml, "w"){|f_new|
f_new.puts(xml_new)
}
end
end
}
# imgFileのリネーム
p img_OldNew
imgFileList.each{|img|
# p img
img_OldNew.each_key{|key|
# p key
if /#{key}/ =~ img
p orig_name = img
p new_name = img.gsub(/#{key}/, img_OldNew[key])
if !dry_run
File.rename(orig_name, new_name)
end
end
}
}
}
Makefile
この Makefile は、a/ 内の構成を見て、自動的に対象ファイルを切り替える。
EPUB_NAME = book.EPUB
SOURCE_DIR = a
# 検出対象ファイル・ディレクトリ
PATTERN_A = META-INF OPS js rights.xml
PATTERN_B = META-INF OEBPS
PATTERN_C = META-INF item js mimetype rights.xml
# mimetype はどちらでも共通
MIMETYPE_FILE = $(SOURCE_DIR)/mimetype
# デフォルトターゲット
all: $(EPUB_NAME)
# EPUB生成ロジック
$(EPUB_NAME): $(MIMETYPE_FILE)
@rm -f $(EPUB_NAME)
@echo "Detecting EPUB structure..."
@if [ -d "$(SOURCE_DIR)/item" ] && [ -d "$(SOURCE_DIR)/js" ] && [ -f "$(SOURCE_DIR)/rights.xml" ]; then \
echo "Using pattern C (item/js/rights.xml)"; \
cd $(SOURCE_DIR) && zip -X0 ../$(EPUB_NAME) mimetype && \
zip -Xr9D ../$(EPUB_NAME) $(PATTERN_C); \
elif [ -d "$(SOURCE_DIR)/OPS" ] && [ -d "$(SOURCE_DIR)/js" ] && [ -f "$(SOURCE_DIR)/rights.xml" ]; then \
echo "Using pattern A (OPS/js/rights.xml)"; \
cd $(SOURCE_DIR) && zip -X0 ../$(EPUB_NAME) mimetype && \
zip -Xr9D ../$(EPUB_NAME) $(PATTERN_A); \
elif [ -d "$(SOURCE_DIR)/OEBPS" ]; then \
echo "Using pattern B (OEBPS only)"; \
cd $(SOURCE_DIR) && zip -X0 ../$(EPUB_NAME) mimetype && \
zip -Xr9D ../$(EPUB_NAME) $(PATTERN_B); \
else \
echo "Error: Could not determine valid EPUB structure in '$(SOURCE_DIR)'."; \
exit 1; \
fi
@echo "EPUB created: $(EPUB_NAME)"
clean:
rm -f $(EPUB_NAME)
使用方法
- この Makefile を a/ の1つ上の階層に配置する。
- ターミナルでその階層へ移動して:
make
おまけ
Windows上のEPUBリーダーは、実は紀伊国屋のKinoppyがおすすめだ。 KnoppyはテキストベースのEPUBの縦書きに対応しており、とても使いやすい。