use of com.github.hmdev.info.ChapterLineInfo in project AozoraEpub3 by hmdev.
the class JConfirmDialog method convert.
/** 変換実行 */
void convert() {
if (jTextTitle.getText().replaceFirst("^[ | ]+", "").replaceFirst("[ | ]+$", "").length() == 0) {
JOptionPane.showMessageDialog(this, "タイトルを設定してください。");
} else {
this.canceled = false;
this.setVisible(false);
//挿絵が選択されていたらファイル名はnull
if (bookInfo.coverImageIndex > -1)
bookInfo.coverFileName = null;
//目次設定
if (!this.bookInfo.isImageOnly() && this.tocDataModel != null) {
//編集中なら確定
if (this.jTableToc.isEditing())
this.jTableToc.getCellEditor().stopCellEditing();
int cnt = this.tocDataModel.getRowCount();
for (int row = 0; row < cnt; row++) {
int lineNum = this.tocDataModel.getLineNum(row) - 1;
ChapterLineInfo chapterLineInfo = bookInfo.getChapterLineInfo(lineNum);
if (chapterLineInfo != null) {
if (!this.tocDataModel.isSelected(row)) {
this.bookInfo.removeChapterLineInfo(chapterLineInfo.lineNum);
} else {
chapterLineInfo.setChapterName(this.tocDataModel.getTocName(row));
}
}
}
}
//表紙情報保存
bookInfo.coverEditInfo = this.jCoverImagePanel.getCoverEditInfo();
}
}
use of com.github.hmdev.info.ChapterLineInfo in project AozoraEpub3 by hmdev.
the class JConfirmDialog method showDialog.
/** 確認ダイアログを表示
* @param location ダイアログ表示位置 */
public void showDialog(File srcFile, String dstPath, String title, String creator, int titleTypeIndex, boolean pubFirst, BookInfo bookInfo, ImageInfoReader imageInfoReader, Point location, int coverW, int coverH) {
//zip内テキストファイル名も表示
String srcFileName = srcFile.getName();
if (bookInfo.textEntryName != null)
srcFileName += " : " + bookInfo.textEntryName.substring(bookInfo.textEntryName.lastIndexOf('/') + 1);
this.jTextSrcFileName.setText(srcFileName);
this.jTextSrcFileName.setToolTipText(srcFileName);
//this.jTextSrcFileName.setCaretPosition(0);
this.jTextDstFileName.setText(dstPath);
this.jTextDstFileName.setToolTipText(dstPath);
//this.jTextDstFileName.setCaretPosition(0);
//メタデータ設定
this.jTextTitle.setText(title);
this.jTextTitleAs.setText(bookInfo.titleAs == null ? "" : bookInfo.titleAs);
this.jTextCreator.setText(creator);
this.jTextCreatorAs.setText(bookInfo.creatorAs == null ? "" : bookInfo.creatorAs);
this.jTextPublisher.setText(bookInfo.publisher == null ? "" : bookInfo.publisher);
//this.jCheckReplaceCover.setSelected(false);
//変更前確認設定用
this.jCheckConfirm2.setSelected(true);
this.jComboTitle.setSelectedIndex(titleTypeIndex);
this.jCheckPubFirst.setSelected(pubFirst);
this.jComboTitle.setEnabled(!bookInfo.isImageOnly());
this.jButtonTitle.setEnabled(!bookInfo.isImageOnly());
//プレビュー読み込み
try {
if (bookInfo.coverImageIndex >= 0 && bookInfo.coverImageIndex < imageInfoReader.countImageFileNames()) {
bookInfo.coverImage = imageInfoReader.getImage(bookInfo.coverImageIndex);
}
if (bookInfo.coverImage == null) {
if (bookInfo.coverFileName == null) {
String srcPath = srcFile.getParent();
File coverFile = new File(srcPath + "/cover.png");
if (!coverFile.exists())
coverFile = new File(srcPath + "/cover.jpg");
if (!coverFile.exists())
coverFile = new File(srcPath + "/cover.jpeg");
if (coverFile.exists())
bookInfo.coverFileName = coverFile.getAbsolutePath();
}
if (bookInfo.coverFileName != null) {
bookInfo.loadCoverImage(bookInfo.coverFileName);
bookInfo.coverImageIndex = -1;
}
}
} catch (Exception e) {
e.printStackTrace();
}
//フラグ初期化
this.canceled = false;
this.skipped = false;
this.coverW = coverW;
this.coverH = coverH;
this.bookInfo = bookInfo;
this.imageInfoReader = imageInfoReader;
//全選択チェック
jCheckChapterAll.setSelected(true);
//目次に無いものはdisabled
jCheckChapterH.setEnabled(false);
jCheckChapterH1.setEnabled(false);
jCheckChapterH2.setEnabled(false);
jCheckChapterH3.setEnabled(false);
jCheckChapterName.setEnabled(false);
jCheckChapterNum.setEnabled(false);
jCheckChapterPattern.setEnabled(false);
jCheckChapterSection.setEnabled(false);
//目次設定
if (bookInfo.isImageOnly()) {
this.tocDataModel = null;
this.jTableToc.setVisible(false);
this.jScrollToc.setVisible(false);
} else {
Vector<ChapterLineInfo> vecChapterLineInfo = bookInfo.getChapterLineInfoList();
this.tocDataModel = this.jTableToc.getModel();
this.tocDataModel.setRowCount(0);
for (ChapterLineInfo chapterLineInfo : vecChapterLineInfo) {
if (chapterLineInfo.pageBreakChapter)
jCheckChapterSection.setEnabled(true);
switch(chapterLineInfo.type) {
case ChapterLineInfo.TYPE_CHUKI_H:
jCheckChapterH.setEnabled(true);
break;
case ChapterLineInfo.TYPE_CHUKI_H1:
jCheckChapterH1.setEnabled(true);
break;
case ChapterLineInfo.TYPE_CHUKI_H2:
jCheckChapterH2.setEnabled(true);
break;
case ChapterLineInfo.TYPE_CHUKI_H3:
jCheckChapterH3.setEnabled(true);
break;
case ChapterLineInfo.TYPE_CHAPTER_NAME:
jCheckChapterName.setEnabled(true);
break;
case ChapterLineInfo.TYPE_CHAPTER_NUM:
jCheckChapterNum.setEnabled(true);
break;
case ChapterLineInfo.TYPE_PATTERN:
jCheckChapterPattern.setEnabled(true);
break;
}
//行の値設定
tocDataModel.addRow(new Object[] { true, chapterLineInfo.pageBreakChapter ? "改" : "", chapterLineInfo.getTypeId(), chapterLineInfo.lineNum + 1, chapterLineInfo.getChapterName() });
}
this.jTableToc.getTableHeader().setPreferredSize(new Dimension(100, 20));
this.jTableToc.setVisible(true);
this.jScrollToc.setVisible(true);
this.jScrollToc.getVerticalScrollBar().setValue(0);
}
if (this.canceled)
return;
//サイズ調整
this.setCoverPaneSize(1);
//表示画像設定
this.jCoverImagePanel.setBookInfo(bookInfo);
this.jButtonScale.setSelected(false);
if (bookInfo.coverEditInfo == null)
this.jCoverImagePanel.setFitType(JCoverImagePanel.FIT_ALL, true);
this.checkPreviewControlEnabled();
this.jCheckReplaceCover.setVisible(bookInfo.insertCoverPage);
//本情報設定ダイアログ表示
if (!this.firstShown)
this.setLocation(location.x + 100, location.y + 20);
this.firstShown = true;
this.setVisible(true);
}
use of com.github.hmdev.info.ChapterLineInfo in project AozoraEpub3 by hmdev.
the class AozoraEpub3Converter method getBookInfo.
/** タイトルと著作者を取得. 行番号も保存して出力時に変換出力
* 章洗濯用に見出しの行もここで取得
* @param src 青空テキストファイルのReader
* @param imageInfoReader テキスト内の画像ファイル名を格納して返却
* @param titleType 表題種別
* @param coverFileName 表紙ファイル名 nullなら表紙無し ""は先頭ファイル "*"は同じファイル名 */
public BookInfo getBookInfo(File srcFile, BufferedReader src, ImageInfoReader imageInfoReader, TitleType titleType, boolean pubFirst) throws Exception {
try {
BookInfo bookInfo = new BookInfo(srcFile);
String line;
this.lineNum = -1;
//前の行のバッファ [1行前, 2行前]
String[] preLines = new String[] { null, null };
//コメントブロック内
boolean inComment = false;
//コメントが始まったらtrue
boolean firstCommentStarted = false;
//最初のコメント開始行
int firstCommentLineNum = -1;
//先頭行
String[] firstLines = new String[10];
//先頭行の開始行番号
int firstLineStart = -1;
//タイトルページ開始行 画像等がなければ-1 タイトルなしは-2
//int preTitlePageBreak = titleType==TitleType.NONE ? -2 : -1;
//コメント内の行数
int commentLineNum = 0;
//コメント開始行
int commentLineStart = -1;
//直線の空行
int lastEmptyLine = -1;
//目次用見出し自動抽出
boolean autoChapter = this.autoChapterName || this.autoChapterNumTitle || this.autoChapterNumOnly || this.autoChapterNumParen || this.autoChapterNumParenTitle || this.chapterPattern != null;
//改ページ後の目次を追加するならtrue
boolean addSectionChapter = true;
//見出し注記の後の文字を追加
boolean addChapterName = false;
//見出しの次の行を繋げるときに見出しの次の行番号を設定
int addNextChapterName = -1;
//ブロック見出し注記、次の行を繋げる場合に設定
ChapterLineInfo preChapterLineInfo = null;
//最後まで回す
while ((line = src.readLine()) != null) {
this.lineNum++;
//見出し等の取得のため前方参照注記は変換 外字文字は置換
line = CharUtils.removeSpace(this.replaceChukiSufTag(this.convertGaijiChuki(line, true, false)));
//注記と画像のチェックなので先にルビ除去
String noRubyLine = CharUtils.removeRuby(line);
//コメント除外 50文字以上をコメントにする
if (noRubyLine.startsWith("--------------------------------")) {
if (!noRubyLine.startsWith("--------------------------------------------------")) {
LogAppender.warn(lineNum, "コメント行の文字数が足りません");
} else {
if (firstCommentLineNum == -1)
firstCommentLineNum = this.lineNum;
//コメントブロックに入ったらタイトル著者終了
firstCommentStarted = true;
if (inComment) {
//コメント行終了
if (commentLineNum > 20)
LogAppender.warn(lineNum, "コメントが " + commentLineNum + " 行 (" + (commentLineStart + 1) + ") -");
commentLineNum = 0;
inComment = false;
continue;
} else {
if (lineNum > 10 && !(commentPrint && commentConvert))
LogAppender.warn(lineNum, "コメント開始行が10行目以降にあります");
//コメント行開始
commentLineStart = this.lineNum;
inComment = true;
continue;
}
}
if (inComment)
commentLineNum++;
}
//空行チェック
if (noRubyLine.equals("") || noRubyLine.equals(" ") || noRubyLine.equals(" ")) {
lastEmptyLine = lineNum;
//空行なので次の行へ
continue;
}
if (inComment && !this.commentPrint)
continue;
//2行前が改ページと画像の行かをチェックして行番号をbookInfoに保存
if (!noIllust)
this.checkImageOnly(bookInfo, preLines, noRubyLine, this.lineNum);
//見出しのChapter追加
if (addChapterName) {
if (//前の見出しがなければ中止
preChapterLineInfo == null)
//前の見出しがなければ中止
addChapterName = false;
else {
String name = this.getChapterName(noRubyLine);
//字下げ注記等は飛ばして次の行を見る
if (name.length() > 0) {
preChapterLineInfo.setChapterName(name);
preChapterLineInfo.lineNum = lineNum;
addChapterName = false;
//次の行を繋げる設定
if (this.useNextLineChapterName)
addNextChapterName = lineNum + 1;
//改ページ後のChapter出力を抑止
addSectionChapter = false;
}
//必ず文字が入る
preChapterLineInfo = null;
}
}
//画像のファイル名の順にimageInfoReaderにファイル名を追加
Matcher m = chukiPattern.matcher(noRubyLine);
while (m.find()) {
String chukiTag = m.group();
String chukiName = chukiTag.substring(2, chukiTag.length() - 1);
if (chukiFlagPageBreak.contains(chukiName)) {
//改ページ注記ならフラグON
addSectionChapter = true;
} else if (chapterChukiMap.containsKey(chukiName)) {
//見出し注記
//注記の後に文字がなければブロックなので次の行 (次の行にブロック注記はこない?)
int chapterType = chapterChukiMap.get(chukiName);
if (noRubyLine.length() == m.start() + chukiTag.length()) {
preChapterLineInfo = new ChapterLineInfo(lineNum + 1, chapterType, addSectionChapter, ChapterLineInfo.getLevel(chapterType), lastEmptyLine == lineNum - 1);
bookInfo.addChapterLineInfo(preChapterLineInfo);
//次の行を見出しとして利用
addChapterName = true;
addNextChapterName = -1;
} else {
bookInfo.addChapterLineInfo(new ChapterLineInfo(lineNum, chapterType, addSectionChapter, ChapterLineInfo.getLevel(chapterType), lastEmptyLine == lineNum - 1, this.getChapterName(noRubyLine.substring(m.end()))));
//次の行を連結
if (this.useNextLineChapterName)
addNextChapterName = lineNum + 1;
//次の行を見出しとして利用しない
addChapterName = false;
}
//改ページ後のChapter出力を抑止
addSectionChapter = false;
}
String lowerChukiTag = chukiTag.toLowerCase();
int imageStartIdx = chukiTag.lastIndexOf('(');
if (imageStartIdx > -1) {
int imageEndIdx = chukiTag.indexOf(")", imageStartIdx);
int imageDotIdx = chukiTag.indexOf('.', imageStartIdx);
//訓点送り仮名チェック #の次が(で.を含まない
if (imageDotIdx > -1 && imageDotIdx < imageEndIdx) {
//画像ファイル名を取得し画像情報を格納
String imageFileName = this.getImageChukiFileName(chukiTag, imageStartIdx);
if (imageFileName != null) {
imageInfoReader.addImageFileName(imageFileName);
if (bookInfo.firstImageLineNum == -1) {
//小さい画像は無視
ImageInfo imageInfo = imageInfoReader.getImageInfo(imageInfoReader.correctExt(imageFileName));
if (imageInfo != null && imageInfo.getWidth() > 64 && imageInfo.getHeight() > 64) {
bookInfo.firstImageLineNum = lineNum;
bookInfo.firstImageIdx = imageInfoReader.countImageFileNames() - 1;
}
}
}
}
} else if (lowerChukiTag.startsWith("<img")) {
//src=の値抽出
String imageFileName = this.getTagAttr(chukiTag, "src");
if (imageFileName != null) {
//画像がなければそのまま追加
imageInfoReader.addImageFileName(imageFileName);
if (bookInfo.firstImageLineNum == -1) {
//小さい画像は無視
ImageInfo imageInfo = imageInfoReader.getImageInfo(imageInfoReader.correctExt(imageFileName));
if (imageInfo != null && imageInfo.getWidth() > 64 && imageInfo.getHeight() > 64) {
bookInfo.firstImageLineNum = lineNum;
bookInfo.firstImageIdx = imageInfoReader.countImageFileNames() - 1;
}
}
}
}
}
//TODO パターンと目次レベルは設定可能にする 空行指定の場合はpreLines利用
if (autoChapter && bookInfo.getChapterLevel(lineNum) == 0) {
//文字列から注記と前の空白を除去
String noChukiLine = CharUtils.removeSpace(CharUtils.removeTag(noRubyLine));
//その他パターン
if (this.chapterPattern != null) {
if (this.chapterPattern.matcher(noChukiLine).find()) {
bookInfo.addChapterLineInfo(new ChapterLineInfo(lineNum, ChapterLineInfo.TYPE_PATTERN, addSectionChapter, ChapterLineInfo.getLevel(ChapterLineInfo.TYPE_PATTERN), lastEmptyLine == lineNum - 1, this.getChapterName(noRubyLine)));
//次の行を連結
if (this.useNextLineChapterName)
addNextChapterName = lineNum + 1;
//改ページ後のChapter出力を抑止
addSectionChapter = false;
}
}
int noChukiLineLength = noChukiLine.length();
if (this.autoChapterName) {
boolean isChapter = false;
//数字を含まない章名
for (int i = 0; i < this.chapterName.length; i++) {
String prefix = this.chapterName[i];
if (noChukiLine.startsWith(prefix)) {
if (noChukiLine.length() == prefix.length()) {
isChapter = true;
break;
} else if (isChapterSeparator(noChukiLine.charAt(prefix.length()))) {
isChapter = true;
break;
}
}
}
//数字を含む章名
if (!isChapter) {
for (int i = 0; i < this.chapterNumPrefix.length; i++) {
String prefix = this.chapterNumPrefix[i];
if (noChukiLine.startsWith(prefix)) {
int idx = prefix.length();
//次が数字かチェック
while (noChukiLineLength > idx && isChapterNum(noChukiLine.charAt(idx))) idx++;
//数字がなければ抽出しない
if (idx <= prefix.length())
break;
//後ろをチェック prefixに対応するsuffixで回す
for (String suffix : this.chapterNumSuffix[i]) {
if (!"".equals(suffix)) {
if (noChukiLine.substring(idx).startsWith(suffix)) {
idx += suffix.length();
if (noChukiLine.length() == idx) {
isChapter = true;
break;
} else if (isChapterSeparator(noChukiLine.charAt(idx))) {
isChapter = true;
break;
}
}
} else {
if (noChukiLine.length() == idx) {
isChapter = true;
break;
} else if (isChapterSeparator(noChukiLine.charAt(idx))) {
isChapter = true;
break;
}
}
}
}
}
}
if (isChapter) {
bookInfo.addChapterLineInfo(new ChapterLineInfo(lineNum, ChapterLineInfo.TYPE_CHAPTER_NAME, addSectionChapter, ChapterLineInfo.getLevel(ChapterLineInfo.TYPE_CHAPTER_NAME), lastEmptyLine == lineNum - 1, this.getChapterName(noRubyLine)));
//次の行を連結
if (this.useNextLineChapterName)
addNextChapterName = lineNum + 1;
//次の行を見出しとして利用
addChapterName = false;
//改ページ後のChapter出力を抑止
addSectionChapter = false;
}
}
if (this.autoChapterNumOnly || this.autoChapterNumTitle) {
//数字
int idx = 0;
while (noChukiLineLength > idx && isChapterNum(noChukiLine.charAt(idx))) idx++;
if (idx > 0) {
if (this.autoChapterNumOnly && noChukiLine.length() == idx || this.autoChapterNumTitle && noChukiLine.length() > idx && isChapterSeparator(noChukiLine.charAt(idx))) {
bookInfo.addChapterLineInfo(new ChapterLineInfo(lineNum, ChapterLineInfo.TYPE_CHAPTER_NUM, addSectionChapter, ChapterLineInfo.getLevel(ChapterLineInfo.TYPE_CHAPTER_NUM), lastEmptyLine == lineNum - 1, this.getChapterName(noRubyLine)));
//次の行を連結
if (this.useNextLineChapterName)
addNextChapterName = lineNum + 1;
//次の行を見出しとして利用しない
addChapterName = false;
//改ページ後のChapter出力を抑止
addSectionChapter = false;
}
}
}
if (this.autoChapterNumParen || this.autoChapterNumParenTitle) {
//括弧内数字のみ
for (int i = 0; i < this.chapterNumParenPrefix.length; i++) {
String prefix = this.chapterNumParenPrefix[i];
if (noChukiLine.startsWith(prefix)) {
int idx = prefix.length();
//次が数字かチェック
while (noChukiLineLength > idx && isChapterNum(noChukiLine.charAt(idx))) idx++;
//数字がなければ抽出しない
if (idx <= prefix.length())
break;
//後ろをチェック
String suffix = this.chapterNumParenSuffix[i];
if (noChukiLine.substring(idx).startsWith(suffix)) {
idx += suffix.length();
if (this.autoChapterNumParen && noChukiLine.length() == idx || this.autoChapterNumParenTitle && noChukiLine.length() > idx && isChapterSeparator(noChukiLine.charAt(idx))) {
bookInfo.addChapterLineInfo(new ChapterLineInfo(lineNum, ChapterLineInfo.TYPE_CHAPTER_NUM, addSectionChapter, 13, lastEmptyLine == lineNum - 1, this.getChapterName(noRubyLine)));
//次の行を連結
if (this.useNextLineChapterName)
addNextChapterName = lineNum + 1;
//次の行を見出しとして利用しない
addChapterName = false;
//改ページ後のChapter出力を抑止
addSectionChapter = false;
}
}
}
}
}
}
//改ページ後の注記以外の本文を追加
if (this.chapterSection && addSectionChapter) {
//底本:は目次に出さない
if (noRubyLine.length() > 2 && noRubyLine.charAt(0) == '底' && noRubyLine.charAt(1) == '本' && noRubyLine.charAt(2) == ':') {
//改ページ後のChapter出力を抑止
addSectionChapter = false;
} else {
//記号のみの行は無視して次の行へ
String name = this.getChapterName(noRubyLine);
if (name.replaceAll("◇|◆|□|■|▽|▼|☆|★|*|+|×|†| ", "").length() > 0) {
bookInfo.addChapterLineInfo(new ChapterLineInfo(lineNum, ChapterLineInfo.TYPE_PAGEBREAK, true, 1, lastEmptyLine == lineNum - 1, name));
if (this.useNextLineChapterName)
addNextChapterName = lineNum + 1;
//改ページ後のChapter出力を抑止
addSectionChapter = false;
}
}
}
//見出しの次の行&見出しでない
if (addNextChapterName == lineNum && bookInfo.getChapterLineInfo(lineNum) == null) {
//見出しの次の行を繋げる
String name = this.getChapterName(noRubyLine);
if (name.length() > 0) {
ChapterLineInfo info = bookInfo.getChapterLineInfo(lineNum - 1);
if (info != null)
info.joinChapterName(name);
}
addNextChapterName = -1;
}
//コメント行の後はタイトル取得はしない
if (!firstCommentStarted) {
String replaced = CharUtils.getChapterName(noRubyLine, 0);
if (firstLineStart == -1) {
//文字の行が来たら先頭行開始
if (replaced.length() > 0) {
firstLineStart = this.lineNum;
firstLines[0] = line;
}
} else {
//改ページで終了
if (isPageBreakLine(noRubyLine))
firstCommentStarted = true;
if (this.lineNum - firstLineStart > firstLines.length - 1) {
firstCommentStarted = true;
} else if (replaced.length() > 0) {
firstLines[this.lineNum - firstLineStart] = line;
}
}
}
//前の2行を保存
preLines[1] = preLines[0];
preLines[0] = noRubyLine;
}
//行数設定
bookInfo.totalLineNum = lineNum;
if (inComment) {
LogAppender.error(commentLineStart, "コメントが閉じていません");
}
//表題と著者を先頭行から設定
bookInfo.setMetaInfo(titleType, pubFirst, firstLines, firstLineStart, firstCommentLineNum);
//タイトルのChapter追加
if (bookInfo.titleLine > -1) {
String name = this.getChapterName(bookInfo.title);
ChapterLineInfo chapterLineInfo = bookInfo.getChapterLineInfo(bookInfo.titleLine);
if (chapterLineInfo == null)
bookInfo.addChapterLineInfo(new ChapterLineInfo(bookInfo.titleLine, ChapterLineInfo.TYPE_TITLE, true, 0, false, name));
else {
chapterLineInfo.type = ChapterLineInfo.TYPE_TITLE;
chapterLineInfo.level = 0;
}
//1行目がタイトルでなければ除外
if (bookInfo.titleLine > 0) {
for (int i = bookInfo.titleLine - 1; i >= 0; i--) bookInfo.removeChapterLineInfo(i);
}
}
if (bookInfo.orgTitleLine > 0)
bookInfo.removeChapterLineInfo(bookInfo.orgTitleLine);
if (bookInfo.subTitleLine > 0)
bookInfo.removeChapterLineInfo(bookInfo.subTitleLine);
if (bookInfo.subOrgTitleLine > 0)
bookInfo.removeChapterLineInfo(bookInfo.subOrgTitleLine);
//前後2行前と2行後に3つ以上に抽出した見出しがある場合連続する見出しを除去
if (this.excludeSeqencialChapter)
bookInfo.excludeTocChapter();
return bookInfo;
} catch (Exception e) {
e.printStackTrace();
LogAppender.error(lineNum, "");
throw e;
}
}
use of com.github.hmdev.info.ChapterLineInfo in project AozoraEpub3 by hmdev.
the class Epub3Writer method write.
/** epubファイルを出力
* @param converter 青空文庫テキスト変換クラス 画像のみの場合と切り替えて利用する
* @param src 青空文庫テキストファイルの入力Stream
* @param srcFile 青空文庫テキストファイル zip時の画像取得用
* @param zipTextFileName zipの場合はzip内のテキストファイルのパス付きファイル名
* @param epubFile 出力ファイル .epub拡張子
* @param bookInfo 書籍情報と縦横書き指定
* @param zipImageFileInfos zipの場合はzip内画像の情報 key=サブフォルダ付きの画像ファイル名
* @throws IOException */
public void write(AozoraEpub3Converter converter, BufferedReader src, File srcFile, String srcExt, File epubFile, BookInfo bookInfo, ImageInfoReader imageInfoReader) throws Exception {
try {
this.canceled = false;
this.bookInfo = bookInfo;
this.imageInfoReader = imageInfoReader;
//インデックス初期化
this.sectionIndex = 0;
this.imageIndex = 0;
this.sectionInfos.clear();
this.chapterInfos.clear();
this.vecGaijiInfo.clear();
this.gaijiNameSet.clear();
this.imageInfos.clear();
this.outImageFileNames.clear();
//Velocity用 共通コンテキスト設定
this.velocityContext = new VelocityContext();
//IDはタイトル著作者のハッシュで適当に生成
String title = bookInfo.title == null ? "" : bookInfo.title;
String creator = bookInfo.creator == null ? "" : bookInfo.creator;
if ("".equals(bookInfo.creator))
bookInfo.creator = null;
//固有ID
velocityContext.put("identifier", UUID.nameUUIDFromBytes((title + "-" + creator).getBytes()));
//表紙の目次表示名
velocityContext.put("cover_name", "表紙");
//タイトル &<>はエスケープ
velocityContext.put("title", CharUtils.escapeHtml(title));
//タイトル読み &<>はエスケープ
if (bookInfo.titleAs != null)
velocityContext.put("titleAs", CharUtils.escapeHtml(bookInfo.titleAs));
//著者 &<>はエスケープ
velocityContext.put("creator", CharUtils.escapeHtml(creator));
//著者読み &<>はエスケープ
if (bookInfo.creatorAs != null)
velocityContext.put("creatorAs", CharUtils.escapeHtml(bookInfo.creatorAs));
//刊行者情報
if (bookInfo.publisher != null)
velocityContext.put("publisher", bookInfo.publisher);
//書籍情報
velocityContext.put("bookInfo", bookInfo);
//更新日時
velocityContext.put("modified", dateFormat.format(bookInfo.modified));
//目次階層化
velocityContext.put("navNest", this.navNest);
//端末種別
if (this.isKindle)
velocityContext.put("kindle", true);
//SVG画像出力
if (this.isSvgImage)
velocityContext.put("svgImage", true);
//スタイル
velocityContext.put("pageMargin", this.pageMargin);
velocityContext.put("bodyMargin", this.bodyMargin);
velocityContext.put("lineHeight", this.lineHeight);
velocityContext.put("fontSize", this.fontSize);
velocityContext.put("boldUseGothic", this.boldUseGothic);
velocityContext.put("gothicUseBold", this.gothicUseBold);
//出力先ePubのZipストリーム生成
zos = new ZipArchiveOutputStream(new BufferedOutputStream(new FileOutputStream(epubFile)));
//mimetypeは非圧縮
//STOREDで格納しCRCとsizeを指定する必要がある
ZipArchiveEntry mimeTypeEntry = new ZipArchiveEntry(MIMETYPE_PATH);
FileInputStream fis = new FileInputStream(new File(templatePath + MIMETYPE_PATH));
byte[] b = new byte[256];
int len = fis.read(b);
fis.close();
CRC32 crc32 = new CRC32();
crc32.update(b, 0, len);
mimeTypeEntry.setMethod(ZipArchiveEntry.STORED);
mimeTypeEntry.setCrc(crc32.getValue());
mimeTypeEntry.setSize(len);
zos.putArchiveEntry(mimeTypeEntry);
zos.write(b, 0, len);
b = null;
zos.closeArchiveEntry();
zos.setLevel(9);
//テンプレートのファイルを格納
for (String fileName : getTemplateFiles()) {
writeFile(zos, fileName);
}
//サブパスの文字長
int archivePathLength = 0;
if (this.bookInfo.textEntryName != null)
archivePathLength = this.bookInfo.textEntryName.indexOf('/') + 1;
//zip出力用Writer
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(zos, "UTF-8"));
//本文を出力
this.writeSections(converter, src, bw, srcFile, srcExt, zos);
if (this.canceled)
return;
//外字のcssを格納
velocityContext.put("vecGaijiInfo", this.vecGaijiInfo);
//スタイルと外字のcssを格納
if (bookInfo.vertical) {
zos.putArchiveEntry(new ZipArchiveEntry(OPS_PATH + CSS_PATH + VERTICAL_TEXT_CSS));
bw = new BufferedWriter(new OutputStreamWriter(zos, "UTF-8"));
Velocity.mergeTemplate(templatePath + OPS_PATH + CSS_PATH + VERTICAL_TEXT_CSS_VM, "UTF-8", velocityContext, bw);
bw.flush();
zos.closeArchiveEntry();
} else {
zos.putArchiveEntry(new ZipArchiveEntry(OPS_PATH + CSS_PATH + HORIZONTAL_TEXT_CSS));
bw = new BufferedWriter(new OutputStreamWriter(zos, "UTF-8"));
Velocity.mergeTemplate(templatePath + OPS_PATH + CSS_PATH + HORIZONTAL_TEXT_CSS_VM, "UTF-8", velocityContext, bw);
bw.flush();
zos.closeArchiveEntry();
}
//表紙をテンプレート+メタ情報から生成 先に出力すると外字画像出力で表紙の順番が狂う
if (!bookInfo.imageOnly && (bookInfo.titlePageType == BookInfo.TITLE_MIDDLE || bookInfo.titlePageType == BookInfo.TITLE_HORIZONTAL)) {
String vmFilePath = templatePath + OPS_PATH + XHTML_PATH + TITLE_M_VM;
if (bookInfo.titlePageType == BookInfo.TITLE_HORIZONTAL) {
converter.vertical = false;
vmFilePath = templatePath + OPS_PATH + XHTML_PATH + TITLE_H_VM;
}
//ルビと外字画像注記と縦中横注記(縦書きのみ)のみ変換する
String line = bookInfo.getTitleText();
if (line != null)
velocityContext.put("TITLE", converter.convertTitleLineToEpub3(line));
line = bookInfo.getSubTitleText();
if (line != null)
velocityContext.put("SUBTITLE", converter.convertTitleLineToEpub3(line));
line = bookInfo.getOrgTitleText();
if (line != null)
velocityContext.put("ORGTITLE", converter.convertTitleLineToEpub3(line));
line = bookInfo.getSubOrgTitleText();
if (line != null)
velocityContext.put("SUBORGTITLE", converter.convertTitleLineToEpub3(line));
line = bookInfo.getCreatorText();
if (line != null)
velocityContext.put("CREATOR", converter.convertTitleLineToEpub3(line));
line = bookInfo.getSubCreatorText();
if (line != null)
velocityContext.put("SUBCREATOR", converter.convertTitleLineToEpub3(line));
line = bookInfo.getSeriesText();
if (line != null)
velocityContext.put("SERIES", converter.convertTitleLineToEpub3(line));
line = bookInfo.getPublisherText();
if (line != null)
velocityContext.put("PUBLISHER", converter.convertTitleLineToEpub3(line));
//package.opf内で目次前に出力
zos.putArchiveEntry(new ZipArchiveEntry(OPS_PATH + XHTML_PATH + TITLE_FILE));
bw = new BufferedWriter(new OutputStreamWriter(zos, "UTF-8"));
Velocity.mergeTemplate(vmFilePath, "UTF-8", velocityContext, bw);
bw.flush();
zos.closeArchiveEntry();
velocityContext.put("title_page", true);
//表題行を目次に出力するならtitle.xhtmlを追加 (本文内の行はchapterinfosに追加されていない)
ChapterLineInfo titleLineInfo = bookInfo.getChapterLineInfo(bookInfo.titleLine);
if (titleLineInfo != null) {
chapterInfos.add(0, new ChapterInfo("title", null, bookInfo.title, ChapterLineInfo.LEVEL_TITLE));
}
}
if (this.canceled)
return;
//表紙データと表示の画像情報
byte[] coverImageBytes = null;
ImageInfo coverImageInfo = null;
if (bookInfo.coverFileName != null && bookInfo.coverFileName.length() > 0) {
//表紙情報をimageInfosに追加
try {
//表紙設定解除
for (ImageInfo imageInfo2 : imageInfos) {
imageInfo2.setIsCover(false);
}
BufferedInputStream bis;
if (bookInfo.coverFileName.startsWith("http")) {
bis = new BufferedInputStream(new URL(bookInfo.coverFileName).openStream(), 8192);
} else {
bis = new BufferedInputStream(new FileInputStream(new File(bookInfo.coverFileName)), 8192);
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
IOUtils.copy(bis, baos);
coverImageBytes = baos.toByteArray();
bis.close();
baos.close();
ByteArrayInputStream bais = new ByteArrayInputStream(coverImageBytes);
coverImageInfo = ImageInfo.getImageInfo(bais);
bais.close();
String ext = coverImageInfo.getExt();
if (isKindle || ext.equals("jpeg"))
ext = "jpg";
coverImageInfo.setId("0000");
coverImageInfo.setOutFileName("0000." + ext);
if (!ext.matches("^(png|jpg|jpeg|gif)$")) {
LogAppender.println("表紙画像フォーマットエラー: " + bookInfo.coverFileName);
coverImageInfo = null;
} else {
coverImageInfo.setIsCover(true);
this.imageInfos.add(0, coverImageInfo);
}
} catch (Exception e) {
e.printStackTrace();
}
} else if (bookInfo.coverImage != null) {
//すべてのページの表紙設定解除
for (ImageInfo imageInfo2 : imageInfos) {
imageInfo2.setIsCover(false);
}
//プレビューでトリミングされた表紙
String ext = "jpg";
if (bookInfo.coverExt != null) {
ext = bookInfo.coverExt;
} else if (bookInfo.coverImageIndex > -1) {
ImageInfo imageInfo = imageInfoReader.getImageInfo(bookInfo.coverImageIndex);
if (imageInfo != null)
ext = imageInfo.getExt();
}
if (isKindle || ext.equals("jpeg"))
ext = "jpg";
coverImageInfo = ImageInfo.getImageInfo(ext, bookInfo.coverImage, -1);
coverImageInfo.setId("0000");
coverImageInfo.setOutFileName("0000." + ext);
coverImageInfo.setIsCover(true);
this.imageInfos.add(0, coverImageInfo);
} else {
//本文にないzip内の表紙を出力対象に追加 (テキストからの相対パス)
if (bookInfo.coverImageIndex > -1 && imageInfoReader.countImageFileNames() > bookInfo.coverImageIndex) {
if (!"txt".equals(srcExt)) {
String imageFileName = imageInfoReader.getImageFileName(bookInfo.coverImageIndex);
if (imageFileName != null) {
ImageInfo imageInfo = imageInfoReader.getImageInfo(imageFileName);
if (imageInfo != null) {
imageFileName = imageFileName.substring(archivePathLength);
outImageFileNames.add(imageFileName);
//表紙フラグも設定
for (ImageInfo imageInfo2 : imageInfos) {
imageInfo2.setIsCover(false);
}
imageInfo.setIsCover(true);
if (!this.imageInfos.contains(imageInfo))
this.imageInfos.add(imageInfo);
}
}
}
}
}
//表紙ページ出力 先頭画像表示時は画像出力時にカバー指定するので出力しない
if (bookInfo.insertCoverPage) {
//追加用の情報取得にのみ使う
ImageInfo insertCoverInfo = coverImageInfo;
if (insertCoverInfo == null && bookInfo.coverImageIndex > -1) {
//本文中の挿絵の場合
insertCoverInfo = imageInfoReader.getImageInfo(bookInfo.coverImageIndex);
if (insertCoverInfo != null) {
insertCoverInfo.setIsCover(true);
if (!bookInfo.imageOnly && insertCoverInfo.getId() == null) {
//zip内の画像で追加処理されていない
this.imageIndex++;
String imageId = decimalFormat.format(this.imageIndex);
insertCoverInfo.setId(imageId);
String ext = insertCoverInfo.getExt();
if (isKindle)
ext = "jpg";
insertCoverInfo.setOutFileName(imageId + "." + ext);
}
}
}
if (insertCoverInfo != null) {
SectionInfo sectionInfo = new SectionInfo("cover-page");
if (this.imageSizeType != SectionInfo.IMAGE_SIZE_TYPE_AUTO) {
//画像が横長なら幅100% それ以外は高さ100%
if ((double) insertCoverInfo.getWidth() / insertCoverInfo.getHeight() >= (double) this.coverW / this.coverH)
sectionInfo.setImageFitW(true);
else
sectionInfo.setImageFitH(true);
} else {
sectionInfo.setImageFitW(false);
sectionInfo.setImageFitH(false);
}
this.velocityContext.put("sectionInfo", sectionInfo);
this.velocityContext.put("coverImage", insertCoverInfo);
zos.putArchiveEntry(new ZipArchiveEntry(OPS_PATH + XHTML_PATH + COVER_FILE));
bw = new BufferedWriter(new OutputStreamWriter(zos, "UTF-8"));
Velocity.mergeTemplate(templatePath + OPS_PATH + XHTML_PATH + COVER_VM, "UTF-8", velocityContext, bw);
bw.flush();
zos.closeArchiveEntry();
} else {
//画像がなかったら表紙ページ無し
bookInfo.insertCoverPage = false;
}
}
//package.opf 出力
velocityContext.put("sections", sectionInfos);
velocityContext.put("images", imageInfos);
zos.putArchiveEntry(new ZipArchiveEntry(OPS_PATH + PACKAGE_FILE));
bw = new BufferedWriter(new OutputStreamWriter(zos, "UTF-8"));
Velocity.mergeTemplate(templatePath + OPS_PATH + PACKAGE_VM, "UTF-8", velocityContext, bw);
bw.flush();
zos.closeArchiveEntry();
//nullを除去
for (int i = chapterInfos.size() - 1; i >= 0; i--) {
if (chapterInfos.get(i).getChapterName() == null)
chapterInfos.remove(i);
}
//表題のレベルを2つめと同じにする
if (bookInfo.insertTitleToc && chapterInfos.size() >= 2) {
chapterInfos.get(0).chapterLevel = chapterInfos.get(1).chapterLevel;
}
//目次の階層情報を設定
//レベルを0から開始に変更
int[] chapterCounts = new int[10];
for (ChapterInfo chapterInfo : chapterInfos) {
chapterCounts[chapterInfo.getChapterLevel()]++;
}
int[] newLevel = new int[10];
int level = 0;
for (int i = 0; i < chapterCounts.length; i++) {
if (chapterCounts[i] > 0)
newLevel[i] = level++;
}
for (ChapterInfo chapterInfo : chapterInfos) {
chapterInfo.chapterLevel = newLevel[chapterInfo.chapterLevel];
}
//開始終了情報を追加 nav用
//レベル0
ChapterInfo preChapterInfo = new ChapterInfo(null, null, null, 0);
for (ChapterInfo chapterInfo : chapterInfos) {
if (preChapterInfo != null) {
//開始
chapterInfo.levelStart = Math.max(0, chapterInfo.chapterLevel - preChapterInfo.chapterLevel);
//終了
preChapterInfo.levelEnd = Math.max(0, preChapterInfo.chapterLevel - chapterInfo.chapterLevel);
}
preChapterInfo = chapterInfo;
}
//一番最後は閉じる
if (chapterInfos.size() > 0) {
ChapterInfo chapterInfo = chapterInfos.lastElement();
if (chapterInfo != null)
chapterInfo.levelEnd = chapterInfo.chapterLevel;
}
int ncxDepth = 1;
if (this.ncxNest) {
int minLevel = 99;
int maxLevel = 0;
//navPointを閉じる回数をlevelEndに設定
//navPointを開始したレベルidxに1を設定
int[] navPointLevel = new int[10];
preChapterInfo = null;
for (ChapterInfo chapterInfo : chapterInfos) {
if (preChapterInfo != null) {
int preLevel = preChapterInfo.chapterLevel;
int curLevel = chapterInfo.chapterLevel;
minLevel = Math.min(minLevel, curLevel);
maxLevel = Math.max(maxLevel, curLevel);
navPointLevel[preLevel] = 1;
if (preLevel < curLevel) {
//前より小さい場合
preChapterInfo.navClose = 0;
} else if (preLevel > curLevel) {
//前より大きい
int close = 0;
for (int i = curLevel; i < navPointLevel.length; i++) {
if (navPointLevel[i] == 1) {
close++;
navPointLevel[i] = 0;
}
}
preChapterInfo.navClose = close;
} else {
preChapterInfo.navClose = 1;
navPointLevel[preLevel] = 0;
}
}
preChapterInfo = chapterInfo;
}
if (minLevel < maxLevel)
ncxDepth = maxLevel - minLevel + 1;
//一番最後は閉じる
if (chapterInfos.size() > 0) {
ChapterInfo chapterInfo = chapterInfos.lastElement();
if (chapterInfo != null) {
int close = 1;
for (int i = 0; i < navPointLevel.length; i++) {
if (navPointLevel[i] == 1) {
close++;
}
}
chapterInfo.navClose = close;
}
}
}
//velocityに設定 1~
velocityContext.put("ncx_depth", ncxDepth);
//出力前に縦中横とエスケープ処理
if (!bookInfo.imageOnly) {
converter.vertical = bookInfo.tocVertical;
int spaceHyphenation = converter.getSpaceHyphenation();
converter.setSpaceHyphenation(0);
StringBuilder buf = new StringBuilder();
for (ChapterInfo chapterInfo : chapterInfos) {
buf.setLength(0);
String converted = CharUtils.escapeHtml(chapterInfo.getChapterName());
if (bookInfo.tocVertical) {
converted = converter.convertTcyText(converted);
}
chapterInfo.setChapterName(converted);
}
//戻す
converter.vertical = bookInfo.vertical;
converter.setSpaceHyphenation(spaceHyphenation);
}
//navファイル
velocityContext.put("chapters", chapterInfos);
zos.putArchiveEntry(new ZipArchiveEntry(OPS_PATH + XHTML_PATH + XHTML_NAV_FILE));
bw = new BufferedWriter(new OutputStreamWriter(zos, "UTF-8"));
Velocity.mergeTemplate(templatePath + OPS_PATH + XHTML_PATH + XHTML_NAV_VM, "UTF-8", velocityContext, bw);
bw.flush();
zos.closeArchiveEntry();
//tocファイル
velocityContext.put("chapters", chapterInfos);
zos.putArchiveEntry(new ZipArchiveEntry(OPS_PATH + TOC_FILE));
bw = new BufferedWriter(new OutputStreamWriter(zos, "UTF-8"));
Velocity.mergeTemplate(templatePath + OPS_PATH + TOC_VM, "UTF-8", velocityContext, bw);
bw.flush();
zos.closeArchiveEntry();
if (src != null)
src.close();
if (this.canceled)
return;
//プログレスバーにテキスト進捗分を追加
if (this.jProgressBar != null && !bookInfo.imageOnly)
this.jProgressBar.setValue(bookInfo.totalLineNum / 10);
//フォントファイル格納
if (!bookInfo.imageOnly) {
File fontsPath = new File(templatePath + OPS_PATH + FONTS_PATH);
if (fontsPath.exists()) {
for (File fontFile : fontsPath.listFiles()) {
String outFileName = OPS_PATH + FONTS_PATH + fontFile.getName();
zos.putArchiveEntry(new ZipArchiveEntry(outFileName));
fis = new FileInputStream(new File(templatePath + outFileName));
IOUtils.copy(fis, zos);
fis.close();
zos.closeArchiveEntry();
}
}
}
//外字ファイル格納
for (GaijiInfo gaijiInfo : this.vecGaijiInfo) {
File gaijiFile = gaijiInfo.getFile();
if (gaijiFile.exists()) {
String outFileName = OPS_PATH + GAIJI_PATH + gaijiFile.getName();
zos.putArchiveEntry(new ZipArchiveEntry(outFileName));
fis = new FileInputStream(gaijiFile);
IOUtils.copy(fis, zos);
fis.close();
zos.closeArchiveEntry();
}
}
zos.setLevel(0);
//表紙編集時のイメージ出力
if (coverImageInfo != null) {
try {
//kindleの場合は常にjpegに変換
if (isKindle) {
String imgExt = coverImageInfo.getExt();
if (!imgExt.startsWith("jp")) {
if (bookInfo.coverImage == null) {
ByteArrayInputStream bais = new ByteArrayInputStream(coverImageBytes);
bookInfo.coverImage = ImageUtils.readImage(imgExt, bais);
bais.close();
}
coverImageInfo.setExt("jpeg");
}
}
if (bookInfo.coverImage != null) {
//プレビューで編集されている場合
zos.putArchiveEntry(new ZipArchiveEntry(OPS_PATH + IMAGES_PATH + coverImageInfo.getOutFileName()));
this.writeCoverImage(bookInfo.coverImage, zos, coverImageInfo);
zos.closeArchiveEntry();
//同じ画像が使われている場合は以後はファイルから読み込ませる
bookInfo.coverImage = null;
} else {
ByteArrayInputStream bais = new ByteArrayInputStream(coverImageBytes);
zos.putArchiveEntry(new ZipArchiveEntry(OPS_PATH + IMAGES_PATH + coverImageInfo.getOutFileName()));
this.writeCoverImage(bais, zos, coverImageInfo);
zos.closeArchiveEntry();
bais.close();
}
//カバー画像は出力済みなので削除
imageInfos.remove(0);
if (this.jProgressBar != null)
this.jProgressBar.setValue(this.jProgressBar.getValue() + 10);
} catch (Exception e) {
e.printStackTrace();
LogAppender.error("表紙画像取得エラー: " + bookInfo.coverFileName);
}
}
if (this.canceled)
return;
//本文画像出力 (画像のみの場合は出力済)
if ("txt".equals(srcExt)) {
//txtの場合はファイルシステムから取得
for (String srcImageFileName : imageInfoReader.getImageFileNames()) {
//拡張子修正
srcImageFileName = imageInfoReader.correctExt(srcImageFileName);
if (outImageFileNames.contains(srcImageFileName)) {
ImageInfo imageInfo = imageInfoReader.getImageInfo(srcImageFileName);
if (imageInfo == null) {
LogAppender.println("[WARN] 画像ファイルなし: " + srcImageFileName);
} else {
File imageFile = imageInfoReader.getImageFile(srcImageFileName);
if (imageFile.exists()) {
fis = new FileInputStream(imageFile);
zos.putArchiveEntry(new ZipArchiveEntry(OPS_PATH + IMAGES_PATH + imageInfo.getOutFileName()));
this.writeImage(new BufferedInputStream(fis, 8192), zos, imageInfo);
zos.closeArchiveEntry();
fis.close();
outImageFileNames.remove(srcImageFileName);
}
}
}
if (this.canceled)
return;
if (this.jProgressBar != null)
this.jProgressBar.setValue(this.jProgressBar.getValue() + 10);
}
} else if (!bookInfo.imageOnly) {
if ("rar".equals(srcExt)) {
////////////////////////////////
//Rar
Archive archive = new Archive(srcFile);
try {
for (FileHeader fileHeader : archive.getFileHeaders()) {
if (!fileHeader.isDirectory()) {
String entryName = fileHeader.getFileNameW();
if (entryName.length() == 0)
entryName = fileHeader.getFileNameString();
entryName = entryName.replace('\\', '/');
//アーカイブ内のサブフォルダは除外してテキストからのパスにする
String srcImageFileName = entryName.substring(archivePathLength);
if (outImageFileNames.contains(srcImageFileName)) {
InputStream is = archive.getInputStream(fileHeader);
try {
this.writeArchiveImage(srcImageFileName, is);
} finally {
is.close();
}
}
}
}
} finally {
archive.close();
}
} else {
////////////////////////////////
//Zip
ZipArchiveInputStream zis = new ZipArchiveInputStream(new BufferedInputStream(new FileInputStream(srcFile), 65536), "MS932", false);
try {
ArchiveEntry entry;
while ((entry = zis.getNextZipEntry()) != null) {
//アーカイブ内のサブフォルダは除外してテキストからのパスにする
String srcImageFileName = entry.getName().substring(archivePathLength);
if (outImageFileNames.contains(srcImageFileName)) {
this.writeArchiveImage(srcImageFileName, zis);
}
}
} finally {
zis.close();
}
}
}
//エラーがなければ100%
if (this.jProgressBar != null)
this.jProgressBar.setValue(this.jProgressBar.getMaximum());
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
//ePub3出力ファイルを閉じる
if (zos != null)
zos.close();
} catch (Exception e) {
e.printStackTrace();
}
//メンバ変数解放
this.velocityContext = null;
this.bookInfo = null;
this.imageInfoReader = null;
}
}
use of com.github.hmdev.info.ChapterLineInfo in project AozoraEpub3 by hmdev.
the class AozoraEpub3Converter method printLineBuffer.
/** 行の文字列を出力
* 改ページフラグがあれば改ページ処理を行う
* @param out 出力先
* @param buf 出力する行
* @param noBr pタグで括れない次以降の行で閉じるブロック注記がある場合
* @param chapterLevel Chapterレベル 指定無し=0, 大見出し=1, 中見出し=2, 見出し=2, 小見出し=3 (パターン抽出時は設定に合わせるか目次リストで選択したレベル)
* @throws IOException */
private void printLineBuffer(BufferedWriter out, StringBuilder buf, int lineNum, boolean noBr) throws IOException {
String line = buf.toString();
int length = buf.length();
//すべて空白は空行にする
if (CharUtils.isSpace(line)) {
line = "";
length = 0;
}
int idIdx = 1;
String chapterId = null;
ChapterLineInfo chapterLineInfo = null;
//空白除去の時はスペースのみの行は空行扱い
if (this.removeEmptyLine > 0 && length > 0 && CharUtils.isSpace(line)) {
line = "";
length = 0;
}
if (length == 0) {
//空行なら行数をカウント 左右中央の時の本文前の空行は無視
if (!this.skipMiddleEmpty && !noBr) {
this.printEmptyLines++;
}
//バッファクリア
buf.setLength(0);
return;
}
//バッファ内の文字列出力
//見出し階層レベル
chapterLineInfo = this.bookInfo.getChapterLineInfo(lineNum);
//タグの階層をチェック (強制改ページ判別用に先にやっておく)
int tagStart = 0;
int tagEnd = 0;
boolean inTag = false;
for (int i = 0; i < length; i++) {
if (inTag) {
if (line.charAt(i) == '/' && line.charAt(i + 1) == '>')
tagEnd++;
if (line.charAt(i) == '>')
inTag = false;
} else {
if (line.charAt(i) == '<') {
if (i < length - 1 && line.charAt(i + 1) == '/')
tagEnd++;
else
tagStart++;
inTag = true;
}
}
}
if (out != null) {
//改ページトリガが設定されていない&タグの外
if (this.forcePageBreak && this.pageBreakTrigger == null && this.tagLevel == 0) {
//行単位で強制改ページ
if (this.pageByteSize > this.forcePageBreakSize) {
this.setPageBreakTrigger(pageBreakNoChapter);
} else {
if (forcePageBreakEmptyLine > 0 && this.printEmptyLines >= forcePageBreakEmptyLine && this.pageByteSize > this.forcePageBreakEmptySize) {
//空行での分割
this.setPageBreakTrigger(pageBreakNoChapter);
} else if (forcePageBreakChapterLevel > 0 && this.pageByteSize > this.forcePageBreakChapterSize) {
//章での分割 次の行が見出しで次の行がタグの中になる場合1行前で改ページ
if (chapterLineInfo != null)
this.setPageBreakTrigger(pageBreakNoChapter);
else if (tagStart - tagEnd > 0 && this.bookInfo.getChapterLevel(lineNum + 1) > 0)
this.setPageBreakTrigger(pageBreakNoChapter);
}
}
}
//改ページフラグが設定されていて、空行で無い場合
if (this.pageBreakTrigger != null) {
//改ページ処理
if (this.pageBreakTrigger.pageType != PageBreakType.PAGE_NORMAL) {
//左右中央
this.writer.nextSection(out, lineNum, this.pageBreakTrigger.pageType, PageBreakType.IMAGE_PAGE_NONE, null);
} else {
//その他
this.writer.nextSection(out, lineNum, PageBreakType.PAGE_NORMAL, this.pageBreakTrigger.imagePageType, this.pageBreakTrigger.srcFileName);
}
//ページ情報初期化
this.pageByteSize = 0;
this.sectionCharLength = 0;
if (tagLevel > 0)
LogAppender.error(lineNum, "タグが閉じていません");
this.tagLevel = 0;
this.lineIdNum = 0;
this.pageBreakTrigger = null;
}
this.skipMiddleEmpty = false;
//空行は行数がカウントされているので文字出力前に出力
if (this.printEmptyLines > 0) {
String br = chukiMap.get("改行")[0];
int lines = Math.min(this.maxEmptyLine, this.printEmptyLines - this.removeEmptyLine);
//見出し後3行以内開始の空行は1行は残す
if (lastChapterLine >= lineNum - this.printEmptyLines - 2) {
lines = Math.max(1, lines);
}
for (int i = lines - 1; i >= 0; i--) {
out.write("<p>");
out.write(br);
out.write("</p>\n");
}
this.pageByteSize += (br.length() + 8) * lines;
this.printEmptyLines = 0;
}
this.lineIdNum++;
if (noBr) {
//見出し用のID設定
if (chapterLineInfo != null) {
chapterId = "kobo." + this.lineIdNum + "." + (idIdx++);
if (line.startsWith("<")) {
//タグがあるのでIDを設定
line = line.replaceFirst("(<[\\d|\\w]+)", "$1 id=\"" + chapterId + "\"");
} else {
//タグでなければ一文字目をspanに入れる
out.write("<span id=\"" + chapterId + "\">" + line.charAt(0) + "</span>");
this.pageByteSize += (chapterId.length() + 20);
line = line.substring(1);
}
}
} else {
//改行用のp出力 見出しなら強制ID出力 koboの栞用IDに利用可能なkobo.のIDで出力
if (this.withMarkId || (chapterLineInfo != null && !chapterLineInfo.pageBreakChapter)) {
chapterId = "kobo." + this.lineIdNum + "." + (idIdx++);
out.write("<p id=\"" + chapterId + "\">");
this.pageByteSize += (chapterId.length() + 14);
} else {
out.write("<p>");
this.pageByteSize += 7;
}
}
out.write(line);
//ページバイト数加算
if (this.forcePageBreak)
this.pageByteSize += line.getBytes("UTF-8").length;
//改行のpを閉じる
if (!noBr) {
out.write("</p>\n");
}
//見出しのChapterをWriterに追加 同じ行で数回呼ばれるので初回のみ
if (chapterLineInfo != null && lastChapterLine != lineNum) {
String name = chapterLineInfo.getChapterName();
if (name != null && name.length() > 0) {
//自動抽出で+10されているのは1桁のレベルに戻す
if (chapterLineInfo.pageBreakChapter)
this.writer.addChapter(null, name, chapterLineInfo.level % 10);
else
this.writer.addChapter(chapterId, name, chapterLineInfo.level % 10);
lastChapterLine = lineNum;
}
}
this.sectionCharLength += length;
}
//タグの階層を変更
this.tagLevel += tagStart - tagEnd;
//バッファクリア
buf.setLength(0);
}
Aggregations