[初心者のJava] iText7を試してみる (2)
ということで、iText7を試してみるの続きです。
今回もコードはGitHubに置いてあります。
Paragraphオブジェクト
前回は文書全体を単一のParagraphオブジェクトとして配置していましたが、今回はとりあえず行ごとにParagraphオブジェクトを生成してみることにしました。
コードはE04.javaにあります。
BufferedReader reader = new BufferedReader(new FileReader(SOURCE_TXT));
String buf;
while ((buf = reader.readLine()) != null) {Text text = new Text(buf);
text.setSplitCharacters(
(glyphLine, glyphPos)->{
if (gyoumatsuKinsoku.indexOf(glyphLine.get(glyphPos).getUnicode()) >= 0)
return false;
if (glyphPos < glyphLine.size() - 1) {
if (gyoutouKinsoku.indexOf(glyphLine.get(glyphPos + 1).getUnicode()) >= 0) {
return false;
}
}
return true;
});
Paragraph p = new Paragraph("")
.setFont(mainfont)
.setFontSize(10)
.add(text);
p.setProperty(Property.TEXT_ALIGNMENT, TextAlignment.JUSTIFIED);
document.add(p);
}
reader.close();
document.close();
前回のサンプルではFiles.readAllBytes()を使用してファイルを一括で読み込んでいましたが、今回はBufferedReaderを使用し、readLine()で1行ずつ読み込むようにしています。
1行目はUTF-8で符号化されたテキストファイルを読み込む際の定石の書き方らしいです。3行目のreadLine()はBufferedReaderオブジェクトが用意している1行読み出しのメソッドで、ファイルを読み終わるとnullが返されるので終了判定も例にあるように簡単にできます。
それ以降はE03.javaのサンプルコードと同じもので特に変更は行っていません。
今回は特に試していませんが、Paragraphオブジェクトには段落の余白を変えるメソッドなどが用意されているので、段落間の空き量を調整するといったこともできると思います。
段落フォントの変更
ここからが新しいトライとなります。
まず、段落フォントを段落ごとに変えるということをやってみました。
コードはE05.javaになります。
サンプル文書として使用している日本国憲法は章タイトルとして「第XX章」で始まる文字列が使用されており、条文の前に必要に応じて「〔〕」でくくられたサブタイトルがおかれています。
まず、章タイトル、サブタイトル、本文でフォントを変えてみることにします。
章タイトル用にはNoto-Sans Mediumを指定したPdfFontオブジェクトheadlineFont、サブタイトル用にはNoto-Sans Regularを指定したPdfFontオブジェクトleaderFont、本文用にはNoto-Serif Regularを指定したPdfFontオブジェクトbodyFontを用意しました。
また、章タイトルの段落とサブタイトルの段落を検出するために牛刀をもって鶏を割くくらいの大げさ感がありますが、以下のような感じで正規表現を使ってみました。
Pattern chapter = Pattern.compile("^第[一二三四五六七八九十]+章.*");
Pattern leader = Pattern.compile("^〔.*〕");
当然ながらPatternオブジェクトは各行を処理するwhileループの外側に置いています。
そして各行の文字列を処理するwhileループの先頭で以下のように正規表現の検査を実行しています。
Matcher mChapter = chapter.matcher(buf);
Matcher mLeader = leader.matcher(buf);
最後にParagraphオブジェクトを生成するところで検査結果を使用してフォントの選択を行っています。
Paragraph p = new Paragraph("")
.setFont(mChapter.matches()? headlineFont: mLeader.matches()? leaderFont: bodyFont)
.setFontSize(mChapter.matches()? 12: 10).add(text);
Patternオブジェクトによる正規表現の検査は3段階で実行されます。
まず、Pattern.compileで正規表現文字列からPatternオブジェクトを生成します。
次にPatternオブジェクトのmatcherメソッドで引数で与えられた文字列を検査するMatcherオブジェクトを生成します。
最後にMatcherオブジェクトのmatchesメソッドを使用して文字列全体が正規表現と適合するかを検査しています。
そしてそれぞれの結果に従いParagraphオブジェクトに指定するフォントとフォントサイズを決定しています。
以上を組み込んだwhileループ周りの全体像はこんな感じです。
BufferedReader reader = new BufferedReader(new FileReader(SOURCE_TXT));
String buf;
Pattern chapter = Pattern.compile("^第[一二三四五六七八九十]+章.*");Pattern leader = Pattern.compile("^〔.*〕");
while ((buf = reader.readLine()) != null) {
Matcher mChapter = chapter.matcher(buf);
Matcher mLeader = leader.matcher(buf);
Text text = new Text(buf);
text.setSplitCharacters(
(glyphLine, glyphPos)->{
if (gyoumatsuKinsoku.indexOf(glyphLine.get(glyphPos).getUnicode()) >= 0)
return false;
if (glyphPos < glyphLine.size() - 1) {
if (gyoutouKinsoku.indexOf(glyphLine.get(glyphPos + 1).getUnicode()) >= 0) {
return false;
}
}
return true;
});
Paragraph p = new Paragraph("")
.setFont(mChapter.matches()? headlineFont:
mLeader.matches()? leaderFont: bodyFont)
.setFontSize(mChapter.matches()? 12: 10)
.add(text);
p.setProperty(Property.TEXT_ALIGNMENT, TextAlignment.JUSTIFIED);
document.add(p);
}
reader.close();
document.close();
段落単位のフォント指定はできたので、次は文字単位のフォント指定をやってみたいと思います。
文字単位のフォント指定
文字単位のフォント指定はTextオブジェクトに対してフォントを指定することで実現できます。
コードはE06.javaにあります。
文字単位のフォント指定ということで各条文の「第XX条」の部分を本文と異なるフォントにしてみたいと思います。
コードはこんな感じです。
1: BufferedReader reader = new BufferedReader(new FileReader(SOURCE_TXT));
2: String buf;
3: Pattern chapter = Pattern.compile("^第[一二三四五六七八九十]+章.*");4: Pattern leader = Pattern.compile("^〔.*〕");
5: Pattern articleNumber = Pattern.compile("^第[一二三四五六七八九十百]+条 ");
6: ISplitCharacters kinsoku = new ISplitCharacters() {
7: @Override
8: public boolean isSplitCharacter(GlyphLine glyphLine, int glyphPos) {
9: if (gyoumatsuKinsoku.indexOf(glyphLine.get(glyphPos).getUnicode()) >= 0)
10: return false;
11: if (glyphPos < glyphLine.size() - 1) {
12: if (gyoutouKinsoku.indexOf(glyphLine.get(glyphPos + 1).getUnicode()) >= 0)
13: return false;
14: }
15: return true;
16: }
17: };
18: while ((buf = reader.readLine()) != null) {
19: Matcher mChapter = chapter.matcher(buf);
20: Matcher mLeader = leader.matcher(buf);
21: Matcher mArticleNumber = articleNumber.matcher(buf);
22: Text text = null;
23: Text moreText = null;
24: if (mArticleNumber.lookingAt()) {
25: text = new Text(mArticleNumber.group());
26: moreText = new Text(buf.substring(mArticleNumber.end()));
27: } else {
28: text = new Text(buf);
29: moreText = null;
30: }
31: text.setSplitCharacters(kinsoku);
32: Paragraph p = new Paragraph("")
33: .setFont(mChapter.matches() ? headlineFont : mLeader.matches() ?
34: leaderFont : bodyFont)
35: .setFontSize(mChapter.matches() ? 12 : 10).add(text);
36: if (moreText != null) {
37: text.setFont(leaderFont);
38: moreText.setSplitCharacters(kinsoku);
39: p.add(moreText);
40: }
41: p.setProperty(Property.TEXT_ALIGNMENT, TextAlignment.JUSTIFIED);
42: document.add(p);
43: }
44: reader.close();
45: document.close();
少し長くなりましたが、5行目で「第XX条」を検査するための正規表現PatternオブジェクトarticleNumberを生成しています。
6行目は禁則処理のためのISplitCharactersオブジェクトを生成してkinsokuに保存しています。これまではTextオブジェクトにLambda式で書いていましたが、一つのParagraphオブジェクトに複数のTextオブジェクトを追加する場合があるので、コードの重複を避けるためにオブジェクトを生成しています。
22行目と23行目で2個のText変数textとmoreTextを宣言しています。そして24行目から30行目で「第XX条」で始まる行であればtextに「第XX条」のTextオブジェクトを、moreTextにそれ以降の本文のTextオブジェクトを保存し、「第XX条」で始まらない行であればすべてをtextに保存してmoreTextはnullのままとしています。
32行目から35行目でParagraphオブジェクトを作成し、1個目のTextオブジェクトを設定しています。
続いて36行目から40行目で追加のTextオブジェクト (moreText) がある場合は、Paragraphオブジェクトに追加済みのTextオブジェクト (text) は「第XX条」のはずなので、フォントを変更し、合わせてもう一つのTextオブジェクト (moreText) をParagraphオブジェクトに追加しています。
なお、最初のTextオブジェクト (text) へのフォント設定はParagraphに追加する前に行っても同じ結果となり、Textオブジェクトにフォント設定を行わない場合はParagraphオブジェクトに設定されたフォントが使用され、フォント設定を行った場合はそのフォントが使用されることが確認できました。
以上、テキストの流し込みによるシンプルな文書の作成は割と簡単にできることがわかりました。
次回はもう少し役に立ちそうな何かを作ってみたいと思います。
0コメント