AozoraEpub3の画像タグで、alt属性がなかったのでつけようと調べたこと。
writerクラスにimageinfoを渡す
public String getAlt(String srcFilePath)
{
return this.imageInfoReader.getImageAlt(srcFilePath);
}
imageInforeaderで画像ファイル名とaltテキストの紐付け。
private final Map<String, String> imageAltMap = new LinkedHashMap<>();
public void addImageAlt(String fileName, String altText) {
imageAltMap.put(correctExt(fileName), altText);
}
public String getImageAlt(String fileName) {
return imageAltMap.get(correctExt(fileName));
}
dstFileNameは出力される画像ファイル名 ratioは縦横比 altを追加したい String.format 外字画像は対応しない alt=""をalt="%s"に書き換え
画像 <span><img class="fit" src="%s" alt=""/> 画像幅 <span class="img" style="width:%s%%"><img style="width:100%%" src="%s" alt=""/> 画像 <span><img class="fit" src="%s" alt="%s"/> 画像幅 <span class="img" style="width:%s%%"><img style="width:100%%" src="%s" alt="%s"/>
altTextの追加
private boolean printImageChuki(
BufferedWriter out,
StringBuilder buf,
String srcFileName,
String dstFileName,
boolean hasCaption,
int lineNum,
String altText
) throws IOException
buf.append(String.format(chukiMap.get("画像")[0], dstFileName));
buf.append(String.format(chukiMap.get("画像")[0], dstFileName, altText));
buf.append(String.format(chukiMap.get("画像幅")[0], ratio, dstFileName));
buf.append(String.format(chukiMap.get("画像幅")[0], ratio, dstFileName, altText));
[#石鏃二つの図(fig42154_01.png)入る] ↑ここを説明として取りたい
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);
// --- ★ ここで説明文を抽出 ---
String imageDesc = null;
int startIdx = chukiTag.indexOf('#') + 1; // 「#」の次から
if (startIdx < imageStartIdx) {
imageDesc = chukiTag.substring(startIdx, imageStartIdx).trim();
}
if (imageFileName != null) {
imageInfoReader.addImageFileName(imageFileName);
// --- ★ alt情報を登録 ---
if (imageDesc != null && !imageDesc.isEmpty()) {
imageInfoReader.addImageAlt(imageFileName, imageDesc);
}
// 既存処理
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;
}
}
}
}
}
imageInfoReader に alt属性を保持する機能を追加
private final Map<String, String> imageAltMap = new LinkedHashMap<>();
public void addImageAlt(String fileName, String altText) {
imageAltMap.put(correctExt(fileName), altText);
}
public String getImageAlt(String fileName) {
return imageAltMap.get(correctExt(fileName));
}
あなた:
次のコードを解説して
/** 画像タグを出力
* @return 単ページ出力ならtrue */
private boolean printImageChuki(BufferedWriter out, StringBuilder buf, String srcFileName, String dstFileName, boolean hasCaption, int lineNum) throws IOException
{
//サイズを取得して画面サイズとの%を指定
int imagePageType = this.writer.getImagePageType(srcFileName, this.tagLevel, lineNum, hasCaption);
//サイズを%で指定 倍率指定が無効または画像が小さいなら0
double ratio = this.writer.getImageWidthRatio(srcFileName, hasCaption);
if (imagePageType == PageBreakType.IMAGE_INLINE_W) {
if (ratio <= 0) buf.append(String.format(chukiMap.get("画像横")[0], dstFileName));
else buf.append(String.format(chukiMap.get("画像幅")[0], ratio, dstFileName));
} else if (imagePageType == PageBreakType.IMAGE_INLINE_H) {
if (ratio <= 0) buf.append(String.format(chukiMap.get("画像縦")[0], dstFileName));
else buf.append(String.format(chukiMap.get("画像幅")[0], ratio, dstFileName));
} else if (imagePageType == PageBreakType.IMAGE_INLINE_TOP_W) {
if (ratio <= 0) buf.append(String.format(chukiMap.get("画像上横")[0], dstFileName));
else buf.append(String.format(chukiMap.get("画像幅上")[0], ratio, dstFileName));
} else if (imagePageType == PageBreakType.IMAGE_INLINE_BOTTOM_W) {
if (ratio <= 0) buf.append(String.format(chukiMap.get("画像下横")[0], dstFileName));
else buf.append(String.format(chukiMap.get("画像幅下")[0], ratio, dstFileName));
} else if (imagePageType == PageBreakType.IMAGE_INLINE_TOP) {
if (ratio <= 0) buf.append(String.format(chukiMap.get("画像上")[0], dstFileName));
else buf.append(String.format(chukiMap.get("画像幅上")[0], ratio, dstFileName));
} else if (imagePageType == PageBreakType.IMAGE_INLINE_BOTTOM) {
if (ratio <= 0) buf.append(String.format(chukiMap.get("画像下")[0], dstFileName));
else buf.append(String.format(chukiMap.get("画像幅下")[0], ratio, dstFileName));
} else if (imagePageType != PageBreakType.IMAGE_PAGE_NONE) {
if (ratio != -1 && this.imageFloatPage) {
//単ページfloat表示
if (imagePageType == PageBreakType.IMAGE_PAGE_W) {
buf.append(String.format(chukiMap.get("画像単横浮")[0], dstFileName));
} else if (imagePageType == PageBreakType.IMAGE_PAGE_H) {
buf.append(String.format(chukiMap.get("画像単縦浮")[0], dstFileName));
} else {
if (ratio <= 0) buf.append(String.format(chukiMap.get("画像単浮")[0], dstFileName));
else buf.append(String.format(chukiMap.get("画像単幅浮")[0], ratio, dstFileName));
}
} else {
//単ページ出力 タグの外のみ
//改ページの前に文字があれば前のページに出力
if (!buf.isEmpty()) this.printLineBuffer(out, buf, lineNum, true);
buf.append(String.format(chukiMap.get("画像")[0], dstFileName));
buf.append(chukiMap.get("画像終わり")[0]);
//単ページ出力
this.printImagePage(out, buf, lineNum, srcFileName, dstFileName, imagePageType);
return true;
}
} else {
if (ratio != -1 && imageFloatBlock) {
//画像float表示
if (ratio <= 0) buf.append(String.format(chukiMap.get("画像浮")[0], dstFileName));
else buf.append(String.format(chukiMap.get("画像幅浮")[0], ratio, dstFileName));
} else {
//画像通常表示
if (ratio <= 0) buf.append(String.format(chukiMap.get("画像")[0], dstFileName));
else buf.append(String.format(chukiMap.get("画像幅")[0], ratio, dstFileName));
}
}
//キャプショがある場合はタグを閉じない
if (hasCaption) {
this.inImageTag = true;
this.nextLineIsCaption = true;
} else {
buf.append(chukiMap.get("画像終わり")[0]);
}
return false;
}
ChatGPT:
このメソッド
private boolean printImageChuki(BufferedWriter out, StringBuilder buf, String srcFileName, String dstFileName, boolean hasCaption, int lineNum)
は、画像タグをHTMLとして出力するためのロジックをまとめたものです。 出力対象の画像の種類(行内・単ページ・上下位置など)や、画像の大きさの比率、キャプションの有無に応じて出力を切り替えています。
テキスト変換処理の中で、
- srcFileName:元画像ファイル名(変換前)
- dstFileName:変換後の出力ファイル名(例:img001.jpg)
をもとに、HTMLに埋め込む
タグやその周囲のHTML構造を生成します。
srcFileName:元画像ファイル名(変換前)
dstFileName:変換後の出力ファイル名(例:img001.jpg)
をもとに、HTMLに埋め込む <img> タグやその周囲のHTML構造を生成します。
結果は StringBuilder buf に追加され、必要に応じて BufferedWriter out に直接出力されます。
| 処理段階 | 内容 |
|---|---|
| 1️⃣ | 画像の表示タイプを取得 (imagePageType) |
| 2️⃣ | 画像の幅倍率(%)を取得 (ratio) |
| 3️⃣ | タイプ別にHTMLテンプレート(chukiMap)を使ってタグを組み立て |
| 4️⃣ | 単ページ表示やfloat表示を考慮 |
| 5️⃣ | キャプション付きならタグを閉じずに状態を記録 |
| 6️⃣ | 必要に応じて true(単ページ出力済み)を返す |
double ratio = this.writer.getImageWidthRatio(srcFileName, hasCaption); int imagePageType = this.writer.getImagePageType(srcFileName, this.tagLevel, lineNum, hasCaption); double ratio = this.writer.getImageWidthRatio(srcFileName, hasCaption);
- getImagePageType(): 画像がどのように表示されるかを判定します。 例:行内、上寄せ、下寄せ、単ページ表示など。 → 戻り値は PageBreakType 定数(IMAGE_INLINE_W, IMAGE_PAGE_W など)
- getImageWidthRatio(): 画像サイズを基準に、ページ幅に対する比率(%)を返します。 倍率指定がなければ 0 または -1。
getImagePageType(): 画像がどのように表示されるかを判定します。 例:行内、上寄せ、下寄せ、単ページ表示など。 → 戻り値は PageBreakType 定数(IMAGE_INLINE_W, IMAGE_PAGE_W など)
getImageWidthRatio(): 画像サイズを基準に、ページ幅に対する比率(%)を返します。 倍率指定がなければ 0 または -1。
以下のように分岐しています。
if (ratio <= 0) buf.append(String.format(chukiMap.get("画像横")[0], dstFileName));
else buf.append(String.format(chukiMap.get("画像幅")[0], ratio, dstFileName));
}
if (imagePageType == PageBreakType.IMAGE_INLINE_W) {
if (ratio <= 0) buf.append(String.format(chukiMap.get("画像横")[0], dstFileName));
else buf.append(String.format(chukiMap.get("画像幅")[0], ratio, dstFileName));
}
- "画像横" や "画像幅" は chukiMap に登録されたHTMLテンプレート文字列。
例:
のような形式。
"画像横" や "画像幅" は chukiMap に登録されたHTMLテンプレート文字列。
例: のような形式。
他の IMAGE_INLINE_H, IMAGE_INLINE_TOP, IMAGE_INLINE_BOTTOM も同様で、 縦方向・上下寄せなどのレイアウト違いを処理しています。
} else if (imagePageType != PageBreakType.IMAGE_PAGE_NONE) {
ここに入るのは「1枚のページを占有する画像」の場合。 さらに imageFloatPage(フロート表示設定)が有効かで分岐。
buf.append(String.format(chukiMap.get("画像単幅浮")[0], ratio, dstFileName));
}
if (ratio != -1 && this.imageFloatPage) {
buf.append(String.format(chukiMap.get("画像単幅浮")[0], ratio, dstFileName));
}
→ ページの一部にフロート表示(例:本文の横に画像を浮かせる)
buf.append(String.format(chukiMap.get("画像")[0], dstFileName));
buf.append(chukiMap.get("画像終わり")[0]);
this.printImagePage(out, buf, lineNum, srcFileName, dstFileName, imagePageType);
return true;
if (!buf.isEmpty()) this.printLineBuffer(out, buf, lineNum, true);
buf.append(String.format(chukiMap.get("画像")[0], dstFileName));
buf.append(chukiMap.get("画像終わり")[0]);
this.printImagePage(out, buf, lineNum, srcFileName, dstFileName, imagePageType);
return true;
- buf にまだ文字列が残っていたらそれを先に出力。 (画像前に文字があると同じページに残る)
- 画像用のタグを組み立て。
- printImagePage() で画像専用ページを生成。
- 単ページ出力完了を示して true を返す。
buf にまだ文字列が残っていたらそれを先に出力。 (画像前に文字があると同じページに残る)
画像用のタグを組み立て。
printImagePage() で画像専用ページを生成。
単ページ出力完了を示して true を返す。
単ページでもfloatでもない場合。
buf.append(String.format(chukiMap.get("画像幅浮")[0], ratio, dstFileName));
} else {
buf.append(String.format(chukiMap.get("画像幅")[0], ratio, dstFileName));
}
if (ratio != -1 && imageFloatBlock) {
buf.append(String.format(chukiMap.get("画像幅浮")[0], ratio, dstFileName));
} else {
buf.append(String.format(chukiMap.get("画像幅")[0], ratio, dstFileName));
}
→ 本文中の通常画像。float設定によって左右寄せを行う。
this.inImageTag = true;
this.nextLineIsCaption = true;
} else {
buf.append(chukiMap.get("画像終わり")[0]);
}
if (hasCaption) {
this.inImageTag = true;
this.nextLineIsCaption = true;
} else {
buf.append(chukiMap.get("画像終わり")[0]);
}
キャプションがある場合は <figure> のようなタグを閉じずにおき、
次の行をキャプションとして扱う準備をします。
return false;
- 単ページ出力を行った場合のみ true
- 通常や行内の場合は false
単ページ出力を行った場合のみ true
通常や行内の場合は false
これにより、呼び出し元が「ページを分けたかどうか」を判定できます。
| 項目 | 内容 |
|---|---|
| メソッド名 | printImageChuki |
| 目的 | 画像タグを出力(位置・倍率・キャプション対応) |
| 戻り値 | 単ページ出力を行ったら true |
| 主な分岐 | 行内画像 / 単ページ画像 / 通常画像 |
| 使用データ | PageBreakType, chukiMap, imageFloatPage, imageFloatBlock |
| 特徴 | キャプション対応、float設定、比率指定など細かく制御可能 |
必要であれば、この関数が使う chukiMap や PageBreakType の定義も参照して、 どういうHTMLが出力されるのか具体例付きで解説できます。 希望しますか?
あなた:
alt属性をつける拡張をするには
ChatGPT:
良い質問です。 この printImageChuki() メソッドに alt 属性(代替テキスト) を追加する拡張は、比較的シンプルに実装できます。 ただし、このコードでは 画像タグのテンプレートを chukiMap によって管理している ため、 どこで alt を埋め込むかで設計が変わります。
方法は大きく分けて2通りあります:
| 方法 | 概要 | 特徴 |
|---|---|---|
| A. chukiMap に alt プレースホルダを追加 | テンプレート文字列に %s を追加して対応 | テンプレート定義側でHTML構造を保つ。推奨 |
| B. メソッド内で手動で alt 属性を挿入 | 出力直前に replaceFirst(">"," alt=\"...\">") などを使う | 既存テンプレートを壊さず簡単に追加できる |
たとえば、現在:
chukiMap.put("画像", new String[]{"<img src=\"%s\">"});
を次のようにします:
chukiMap.put("画像幅", new String[]{"<img src=\"%2$s\" style=\"width:%1$.1f%%\" alt=\"%3$s\">"});
chukiMap.put("画像", new String[]{"<img src=\"%s\" alt=\"%s\">"});
chukiMap.put("画像幅", new String[]{"<img src=\"%2$s\" style=\"width:%1$.1f%%\" alt=\"%3$s\">"});
つまり:
- %1$s → 比率
- %2$s → ファイル名
- %3$s → alt テキスト
%1$s → 比率
%2$s → ファイル名
%3$s → alt テキスト
と指定しておきます。
String srcFileName, String dstFileName, boolean hasCaption,
int lineNum, String altText) throws IOException
private boolean printImageChuki(BufferedWriter out, StringBuilder buf,
String srcFileName, String dstFileName, boolean hasCaption,
int lineNum, String altText) throws IOException
例:
buf.append(String.format(chukiMap.get("画像")[0], dstFileName, altText));
else
buf.append(String.format(chukiMap.get("画像幅")[0], ratio, dstFileName, altText));
if (ratio <= 0)
buf.append(String.format(chukiMap.get("画像")[0], dstFileName, altText));
else
buf.append(String.format(chukiMap.get("画像幅")[0], ratio, dstFileName, altText));
呼び出し元で altText を指定します。 たとえばファイル名から自動生成するなら:
printImageChuki(out, buf, srcFile, dstFile, hasCaption, lineNum, getAltText(srcFile));
return new File(srcFileName).getName().replaceFirst("\\..+$", "");
}
private String getAltText(String srcFileName) {
return new File(srcFileName).getName().replaceFirst("\\..+$", "");
}
既存のテンプレートを変えたくない場合、 タグを組み立てた直後に alt 属性を挿入する方法があります。
例:
tag = tag.replaceFirst(">", " alt=\"" + escapeHtml(altText) + "\">");
buf.append(tag);
String tag = String.format(chukiMap.get("画像")[0], dstFileName);
tag = tag.replaceFirst(">", " alt=\"" + escapeHtml(altText) + "\">");
buf.append(tag);
このようにすれば、
→
になります。
alt に日本語や特殊文字が入る場合を考慮して:
return text.replace("&", "&")
.replace("<", "<")
.replace(">", ">")
.replace("\"", """);
}
private String escapeHtml(String text) {
return text.replace("&", "&")
.replace("<", "<")
.replace(">", ">")
.replace("\"", """);
}
| 項目 | 内容 |
|---|---|
| 拡張目的 | |
| 方法A | chukiMapテンプレートに %s を追加(構造が明確・推奨) |
| 方法B | 出力後に replaceFirst(">", " alt=...>") で挿入(簡単) |
| 注意点 | alt はHTMLエスケープすべき |
| 呼び出し側 | altText パラメータを追加・指定 |
もし alt を キャプションと同じ内容にしたい(hasCaption=true のとき) または ファイル名から自動生成したい 場合は、 それに合わせた altText 生成ロジックをこちらで組み込む例も提示できます。
希望しますか?
あなた:
#### 画像 String.formatで出力 ##拡大無し 画像 <span><img class="fit" src="%s" alt=""/> ##回り込み 画像上 <span class="float-start m-end-1em"><img src="%s" alt=""/> 画像下 <span class="float-end m-start-1em"><img src="%s" alt=""/> ##画面サイズ比で幅の%を指定 画像幅 <span class="img" style="width:%s%%"><img style="width:100%%" src="%s" alt=""/> 画像幅上 <span class="img ft" style="width:%s%%"><img style="width:100%%" src="%s" alt=""/> 画像幅下 <span class="img fb" style="width:%s%%"><img style="width:100%%" src="%s" alt=""/> ##画面サイズよりも大きい 横または縦をcssで100%指定 画像横 <span><img class="fit" src="%s" alt=""/> 画像縦 <span><img class="fit" src="%s" alt=""/> ##回り込み幅100% 画像上横 <span class="img ft fw"><img src="%s" alt=""/> 画像下横 <span class="img fb fw"><img src="%s" alt=""/> ##通常画像のfloat表示 画像浮 <span class="img fblk"><img src="%s" alt=""/> 画像幅浮 <span class="img fblk" style="width:%s%%"><img style="width:100%%" src="%s" alt=""/> ##単ページのfloat表示 xhtmlを分割せずに単ページ表示 画面サイズ比で幅の%を指定 画像単浮 <span class="img fpage"><img src="%s"/> 画像単幅浮 <span class="img fpage"><img style="width:%s;" src="%s" alt=""/> 画像単横浮 <span class="img fpage"><img style="width:100%%;" src="%s" alt=""/> 画像単縦浮 <span class="img fpage"><img style="height:100%%;" src="%s" alt=""/> ##画像終了タグ 下にキャプションがあればその後に出力される 画像終わり </span>
ChatGPT:
非常に良い資料です。 これは chukiMap に登録されている 画像出力テンプレート群(String.format 用)ですね。 各テンプレートには %s(プレースホルダ)があり、printImageChuki() で String.format() によって実際の dstFileName(画像ファイル名)や比率(ratio)を埋め込んでいます。
あなたの提示内容を見ると、すでに alt="" 属性が全テンプレートに含まれています。 したがって「alt属性をつける拡張」をするには:
%s プレースホルダを追加し、alt の内容を埋め込むように printImageChuki() を変更する
という形になります。
下記のように altに %s を追加 します:
画像 <span><img class="fit" src="%s" alt="%s"/> ## 回り込み 画像上 <span class="float-start m-end-1em"><img src="%s" alt="%s"/> 画像下 <span class="float-end m-start-1em"><img src="%s" alt="%s"/> ## 幅指定 画像幅 <span class="img" style="width:%s%%"><img style="width:100%%" src="%s" alt="%s"/> 画像幅上 <span class="img ft" style="width:%s%%"><img style="width:100%%" src="%s" alt="%s"/> 画像幅下 <span class="img fb" style="width:%s%%"><img style="width:100%%" src="%s" alt="%s"/> ## 画面サイズより大きい 画像横 <span><img class="fit" src="%s" alt="%s"/> 画像縦 <span><img class="fit" src="%s" alt="%s"/> ## 回り込み幅100% 画像上横 <span class="img ft fw"><img src="%s" alt="%s"/> 画像下横 <span class="img fb fw"><img src="%s" alt="%s"/> ## 通常float 画像浮 <span class="img fblk"><img src="%s" alt="%s"/> 画像幅浮 <span class="img fblk" style="width:%s%%"><img style="width:100%%" src="%s" alt="%s"/> ## 単ページfloat 画像単浮 <span class="img fpage"><img src="%s" alt="%s"/> 画像単幅浮 <span class="img fpage"><img style="width:%s;" src="%s" alt="%s"/> 画像単横浮 <span class="img fpage"><img style="width:100%%;" src="%s" alt="%s"/> 画像単縦浮 <span class="img fpage"><img style="height:100%%;" src="%s" alt="%s"/> ## 終了タグ 画像終わり </span> ## 拡大無し 画像 <span><img class="fit" src="%s" alt="%s"/> ## 回り込み 画像上 <span class="float-start m-end-1em"><img src="%s" alt="%s"/> 画像下 <span class="float-end m-start-1em"><img src="%s" alt="%s"/> ## 幅指定 画像幅 <span class="img" style="width:%s%%"><img style="width:100%%" src="%s" alt="%s"/> 画像幅上 <span class="img ft" style="width:%s%%"><img style="width:100%%" src="%s" alt="%s"/> 画像幅下 <span class="img fb" style="width:%s%%"><img style="width:100%%" src="%s" alt="%s"/> ## 画面サイズより大きい 画像横 <span><img class="fit" src="%s" alt="%s"/> 画像縦 <span><img class="fit" src="%s" alt="%s"/> ## 回り込み幅100% 画像上横 <span class="img ft fw"><img src="%s" alt="%s"/> 画像下横 <span class="img fb fw"><img src="%s" alt="%s"/> ## 通常float 画像浮 <span class="img fblk"><img src="%s" alt="%s"/> 画像幅浮 <span class="img fblk" style="width:%s%%"><img style="width:100%%" src="%s" alt="%s"/> ## 単ページfloat 画像単浮 <span class="img fpage"><img src="%s" alt="%s"/> 画像単幅浮 <span class="img fpage"><img style="width:%s;" src="%s" alt="%s"/> 画像単横浮 <span class="img fpage"><img style="width:100%%;" src="%s" alt="%s"/> 画像単縦浮 <span class="img fpage"><img style="height:100%%;" src="%s" alt="%s"/> ## 終了タグ 画像終わり </span>
変更点:
- 各
に alt="%s" を追加。
- 幅指定テンプレート(画像幅*)では %s%%(幅)→ %s%%(幅), %s(src), %s(alt)と3引数対応に変更。
各 <img> に alt="%s" を追加。
幅指定テンプレート(画像幅*)では %s%%(幅)→ %s%%(幅), %s(src), %s(alt)と3引数対応に変更。
新たに altText 引数を追加します:
BufferedWriter out,
StringBuilder buf,
String srcFileName,
String dstFileName,
boolean hasCaption,
int lineNum,
String altText
) throws IOException
private boolean printImageChuki(
BufferedWriter out,
StringBuilder buf,
String srcFileName,
String dstFileName,
boolean hasCaption,
int lineNum,
String altText
) throws IOException
buf.append(String.format(chukiMap.get("画像幅")[0], ratio, dstFileName));
buf.append(String.format(chukiMap.get("画像幅")[0], ratio, dstFileName, altText));
buf.append(String.format(chukiMap.get("画像")[0], dstFileName));
buf.append(String.format(chukiMap.get("画像")[0], dstFileName, altText));
用途に応じて自動生成や指定が可能です。
String altText = new File(srcFileName).getName().replaceFirst("\\..+$", "");
→ img001.jpg → img001
altText = "画像キャプションあり";
if (hasCaption)
altText = "画像キャプションあり";
- alt を追加したことで %s の数が増えるので、 String.format() の引数をすべて更新する必要があります。
- 幅指定テンプレートでは %s%% → 幅(ratio)、続いて src, alt の順に3つ渡す。
alt を追加したことで %s の数が増えるので、 String.format() の引数をすべて更新する必要があります。
幅指定テンプレートでは %s%% → 幅(ratio)、続いて src, alt の順に3つ渡す。
| 目的 | |
|---|---|
| 方法 | chukiMap テンプレートに %s を追加 |
| メソッド変更 | printImageChuki() に altText 引数を追加 |
| 出力例 | |
| 注意点 | String.format の引数順を全テンプレートで統一する |
もし希望があれば、 あなたの既存 printImageChuki() に完全に組み込んだ 修正版コード全体(コンパイル可能版)を提示できます。 出力タグの %s 構成(引数順)を自動的に合わせた形で生成しましょうか?
あなた:
画像の説明を取得したい
//画像のファイル名の順に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; //次の行を見出しとして利用しない
}
addSectionChapter = false; //改ページ後のChapter出力を抑止
}
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;
}
}
}
}
}
ChatGPT:
良いところに注目されています。 このコードは「行テキストから注記タグ(ちゅうきタグ)を検出し、画像ファイル名を抽出して imageInfoReader に登録する」処理です。 つまり、画像タグを検出して src を取得している段階で、そこに 「画像の説明(altやキャプション)」を取得する拡張 を入れることが可能です。
- noRubyLine(ルビ除去済みの行)から chukiPattern で
や #(画像.jpg) のような注記タグを探す
- タグ文字列を chukiTag として取得
- タグの中にファイル名がある場合(src= または (...)) → imageFileName を抽出 → imageInfoReader.addImageFileName(imageFileName) で登録
- 必要に応じて最初の画像を bookInfo に記録
noRubyLine(ルビ除去済みの行)から chukiPattern で <img> や #(画像.jpg) のような注記タグを探す
タグ文字列を chukiTag として取得
タグの中にファイル名がある場合(src= または (...)) → imageFileName を抽出 → imageInfoReader.addImageFileName(imageFileName) で登録
必要に応じて最初の画像を bookInfo に記録
alt や キャプション のような「画像の説明テキスト」を ファイル名と一緒に imageInfoReader へ登録して、 あとで printImageChuki() に渡せるようにする。
すでに 形式のタグを検出している部分があります。
} else if (lowerChukiTag.startsWith("<img")) {
// src=の値抽出
String imageFileName = this.getTagAttr(chukiTag, "src");
if (imageFileName != null) {
imageInfoReader.addImageFileName(imageFileName);
} else if (lowerChukiTag.startsWith("<img")) {
// src=の値抽出
String imageFileName = this.getTagAttr(chukiTag, "src");
if (imageFileName != null) {
imageInfoReader.addImageFileName(imageFileName);
ここで alt も同様に取得できます。
String altText = this.getTagAttr(chukiTag, "alt");
if (altText == null || altText.isEmpty()) {
altText = new File(imageFileName).getName(); // ファイル名を代替
}
imageInfoReader.addImageInfo(imageFileName, altText);
String altText = this.getTagAttr(chukiTag, "alt");
if (altText == null || altText.isEmpty()) {
altText = new File(imageFileName).getName(); // ファイル名を代替
}
imageInfoReader.addImageInfo(imageFileName, altText);
たとえば、次のような注記を想定します:
#(挿絵.jpg:少女が歩いている) #(挿絵.jpg:少女が歩いている)
これなら、:(コロン)以降を説明として扱えます。
int imageStartIdx = chukiTag.lastIndexOf('(');
int imageEndIdx = chukiTag.indexOf(')', imageStartIdx);
String inner = chukiTag.substring(imageStartIdx + 1, imageEndIdx);
String[] parts = inner.split(":", 2);
String imageFileName = parts[0].trim();
String altText = (parts.length > 1) ? parts[1].trim() : "";
int imageStartIdx = chukiTag.lastIndexOf('(');
int imageEndIdx = chukiTag.indexOf(')', imageStartIdx);
String inner = chukiTag.substring(imageStartIdx + 1, imageEndIdx);
String[] parts = inner.split(":", 2);
String imageFileName = parts[0].trim();
String altText = (parts.length > 1) ? parts[1].trim() : "";
そのあと:
imageInfoReader.addImageInfo(imageFileName, altText); imageInfoReader.addImageInfo(imageFileName, altText);
いまはおそらくこんな形:
public void addImageFileName(String fileName) {
imageFileNames.add(fileName);
}
public void addImageFileName(String fileName) {
imageFileNames.add(fileName);
}
これを拡張して:
public void addImageInfo(String fileName, String altText) {
ImageInfo info = getOrCreateImageInfo(fileName);
info.setAltText(altText);
}
public void addImageInfo(String fileName, String altText) {
ImageInfo info = getOrCreateImageInfo(fileName);
info.setAltText(altText);
}
ImageInfo クラスにもフィールドを追加します:
private String altText;
public void setAltText(String altText) {
this.altText = altText;
}
public String getAltText() {
return altText;
}
private String altText;
public void setAltText(String altText) {
this.altText = altText;
}
public String getAltText() {
return altText;
}
先ほどの printImageChuki() に altText 引数を追加した形と組み合わせます。
String altText = imageInfoReader.getImageInfo(dstFileName).getAltText(); printImageChuki(out, buf, srcFileName, dstFileName, hasCaption, lineNum, altText); String altText = imageInfoReader.getImageInfo(dstFileName).getAltText(); printImageChuki(out, buf, srcFileName, dstFileName, hasCaption, lineNum, altText);
| 拡張箇所 | 内容 |
|---|---|
| 1️⃣ chukiTag の解析 | |
| 2️⃣ imageInfoReader | ファイル名+説明テキストをセットで保持 |
| 3️⃣ ImageInfo | altText フィールドを追加 |
| 4️⃣ printImageChuki() | altText を引数で受け取って |
必要であれば、この処理(while (m.find()))に 実際に alt 抽出と imageInfoReader 登録を組み込んだ完全なコード修正版も提示できます。
作りますか?
あなた:
画像注記の例
[#石鏃二つの図(fig42154_01.png)入る]
ChatGPT:
なるほど、ありがとうございます。 つまり次のような注記を処理して、画像ファイル名 だけでなく説明文(キャプション) も取得したいということですね。
[#石鏃二つの図(fig42154_01.png)入る] [#石鏃二つの図(fig42154_01.png)入る]
あなたの既存コードでは、 この注記から ファイル名(fig42154_01.png) だけを抽出しています。
String imageFileName = this.getImageChukiFileName(chukiTag, imageStartIdx); imageInfoReader.addImageFileName(imageFileName); String imageFileName = this.getImageChukiFileName(chukiTag, imageStartIdx); imageInfoReader.addImageFileName(imageFileName);
しかし、注記内の「石鏃二つの図」の部分は無視されています。 この部分を「画像説明(alt属性やキャプション)」として扱いたいわけです。
画像注記から説明部分を取り出すには、 ( の前の部分を抜き出せばOKです。
例:
↑ここを説明として取りたい
[#石鏃二つの図(fig42154_01.png)入る]
↑ここを説明として取りたい
次のように処理を追加します。
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);
// --- ★ ここで説明文を抽出 ---
String imageDesc = null;
int startIdx = chukiTag.indexOf('#') + 1; // 「#」の次から
if (startIdx < imageStartIdx) {
imageDesc = chukiTag.substring(startIdx, imageStartIdx).trim();
}
if (imageFileName != null) {
imageInfoReader.addImageFileName(imageFileName);
// --- ★ alt情報を登録 ---
if (imageDesc != null && !imageDesc.isEmpty()) {
imageInfoReader.addImageAlt(imageFileName, imageDesc);
}
// 既存処理
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;
}
}
}
}
}
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);
// --- ★ ここで説明文を抽出 ---
String imageDesc = null;
int startIdx = chukiTag.indexOf('#') + 1; // 「#」の次から
if (startIdx < imageStartIdx) {
imageDesc = chukiTag.substring(startIdx, imageStartIdx).trim();
}
if (imageFileName != null) {
imageInfoReader.addImageFileName(imageFileName);
// --- ★ alt情報を登録 ---
if (imageDesc != null && !imageDesc.isEmpty()) {
imageInfoReader.addImageAlt(imageFileName, imageDesc);
}
// 既存処理
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;
}
}
}
}
}
private final Map<String, String> imageAltMap = new LinkedHashMap<>();
public void addImageAlt(String fileName, String altText) {
imageAltMap.put(correctExt(fileName), altText);
}
public String getImageAlt(String fileName) {
return imageAltMap.get(correctExt(fileName));
}
private final Map<String, String> imageAltMap = new LinkedHashMap<>();
public void addImageAlt(String fileName, String altText) {
imageAltMap.put(correctExt(fileName), altText);
}
public String getImageAlt(String fileName) {
return imageAltMap.get(correctExt(fileName));
}
String imageFileName = imageInfoReader.getImageFileName(idx);
String altText = imageInfoReader.getImageAlt(imageFileName);
if (altText == null) altText = "";
String html = String.format("<span><img class=\"fit\" src=\"%s\" alt=\"%s\"/></span>",
imageFileName, HtmlEscaper.escape(altText));
String imageFileName = imageInfoReader.getImageFileName(idx);
String altText = imageInfoReader.getImageAlt(imageFileName);
if (altText == null) altText = "";
String html = String.format("<span><img class=\"fit\" src=\"%s\" alt=\"%s\"/></span>",
imageFileName, HtmlEscaper.escape(altText));
入力:
[#石鏃二つの図(fig42154_01.png)入る] [#石鏃二つの図(fig42154_01.png)入る]
出力HTML:
<span><img class="fit" src="fig42154_01.png" alt="石鏃二つの図"/></span> <span><img class="fit" src="fig42154_01.png" alt="石鏃二つの図"/></span>
希望すれば、上記の imageInfoReader の完全な修正版クラス(alt対応版)も出力できます。 必要ですか?