以前の記事は↑このリンクから閲覧できます。
明日(32 日目)の担当者は @uni_kakurenbo 氏です。
どのような人物か知りたければ↑このリンクを押しましょう。
おはようございます。組版の話をしましょう。
きょうは共通テストみたいな組版を試みようと思います。
まず本家を調査して, 次に再現します。
※数式などの込み入った話は後編に回します。
§1. 本家を調査
模倣するにあたり, まず最初に本家を調べましょう。
大学入試センターは, 大学入試共通テストの表紙の参考イメージをウェブ上で公開しています。
例えばこれは, 令和 3 年度の共通テストの国語の表紙 (参考イメージ) です。437 686 バイトあります。これをダウンロードして, 詳しく解析します。
§1.1 PDF の中身
PDF ファイルの中身は以下のような構造になっています。
① PDF のバージョン指定
② オブジェクトの列挙
③ オブジェクトの場所やファイル情報
④ ③がどこから始まるのかの情報
ダウンロードした PDF をバイナリエディタで開くと, 一番最初は %PDF-1.6 で, 確かに①のバージョン指定になっています。このあと第 16 バイトからしばらくオブジェクトの列挙が続いています。
さらに一番下までスクロールすると,
startxref
437262
%%EOF
という部分があります。これが先ほどでいう④にあたります。③は 437 262 バイト目から始まっている, ということです。
③の部分は 2 つの形式があり, xref で始まる独自の構造を持ったクロスリファレンステーブルを使用するか, 通常のオブジェクトを用いて指定するクロスリファレンスストリームを使用することができます。オブジェクトは, オブジェクト番号, 世代番号, obj, 中身, endobj という構造を持っており, 今回のファイルの③は 47 0 obj… で, 2 つの数と obj という文字列で始まりましたから, オブジェクトです。したがって, この PDF ではクロスリファレンステーブルではなくクロスリファレンスストリームが使用されていることがわかります。
47 0 obj
<</DecodeParms<</Columns 5/Predictor 12>>/Filter/FlateDecode ... >>
stream
...
endstream
endobj
§1.2 クロスリファレンスストリーム
PDF のオブジェクトにはいくつかの種類があります。
- boolean… 真と偽の 2 つのみ。
- 数… 数値を直接書く。
- 文字列…
(で始まり)で終わる。文字列中に丸括弧が含まれる場合は\を用いてエスケープする。 - 配列…
[で始まり]で終わる。要素は空白区切りで並べる。 - バイナリ…
<で始まり>で終わる。中に 16 進数を書く。桁数が奇数の場合は末尾に 0 を補って解釈される。 - 名前…
/で始まる。ASCII 文字を並べたものだが, 一部の ASCII 文字は使用できない。 - 辞書…
<<で始まり>>で終わる。中身がなくてもよい。中にキーと値を交互に並べる。ストリームが続く場合があり, このときはendobjの前にstream, ストリーム本体,endstreamが入る。 - 参照… オブジェクト番号, 世代番号,
Rをスペース区切りで書くと, そのオブジェクトへの参照になる。
さて, 今見ている 47 番のオブジェクトは << で始まり >> で終わっているため, 辞書です。その中に /Info 43 0 R とありますから, 文書の情報は 43 番のオブジェクトにあります。また, /Root 7 0 R とありますから, 7 番のオブジェクトが階層構造のトップに存在することもわかります。したがって, これらのオブジェクトがこのファイルのどこに存在するのかを調べる必要があります。そこでこのオブジェクトのストリームを読まなければなりません。
第 437 475 バイトからがストリームです。/Length 166 は, このストリームが 166 バイトであることを意味しています。/Filter/FlateDecode は圧縮方法が Deflate 圧縮であるということです。まずはこの圧縮を解除する必要があります。圧縮方法の詳細はここに書いてあります。
このストリーム 166 バイトのうち, 最初の 2 バイトはヘッダ, 最後の 2 バイトはチェックサムですから, その間の 162 バイトの部分が, 上記のリンク先に書かれたアルゴリズムで圧縮されている部分になります。
圧縮を解除すると, 288 バイトのデータが得られます。ここで先ほどの辞書には /W[1 3 1] とあります。これは, 各オブジェクトに関するデータが 3 つの要素からなり, 第 1 の要素と第 3 の要素は 1 バイトずつ, 第 2 の要素は 3 バイトからなる, という意味です。では 5 バイトごとに区切って読めばいいのかというと, そうではありません。/DecodeParms<</Columns 5/Predictor 12>> に注意が必要です。Predictor が 12 ですから, PNG 予測アルゴリズムにより, 各データは 1 バイトずつ増えていることを意味します。したがって, Columns の 5 より 1 つ多い 6 バイトに区切って読まなければなりません。さらに, このデータはそのまま用いるのではなく, 今までのデータの累積和 (mod 256) を取らなければ値が読めません。
例えば 7 番について調べましょう。まず最初に, 6 バイトのうちの各バイトについて, 0 番から 7 番までの 8 つ分の値を, 全部足します。最初の 1 バイトは 2 が 8 回足されて 16 になりますが, これは関係のない部分です。次の 1 バイトは 1 になります。このオブジェクトが圧縮されていないことを意味します。 続く 3 バイト (4603) がその先頭の位置, 最後の 1 バイト (0) が世代番号です。したがって第 4603 バイトからは 7 0 obj という記述があるはずです。
実際, 第 4603 バイトに移動すると, 7 0 obj があります。
7 0 obj
<</Metadata 4 0 R/OpenAction 42 0 R/PageLayout/OneColumn/Pages 3 0 R/Type/Catalog>>
endobj
このオブジェクトも辞書です。ページ数などの情報は 3 番のオブジェクトにあります。
では今度は 43 番も調べましょう。今度は第 437 071 バイトを参照します。
43 0 obj
<</CreationDate(D:20200515103513Z)/Creator(EdianWing) ... >>
endobj
この辞書には Creator という項目があり, 中に EdianWing とありますから, 共通テストは EdianWing で組版されていることがわかりました。NEC ネクサソリューションズの出していた, Windows 用組版ツールです。
§1.3 ページ情報
ページに関する情報を調べましょう。先ほどのクロスリファレンスストリームを見て, 足し算をすると, 今度は第 1 要素が 2 になります。このオブジェクトが圧縮されていることを意味します。第 2 要素が 44, 第 3 要素が 1 ですから, 3 番のオブジェクトは, 44 番のオブジェクトについたストリームのインデックス 1 にあることになります。したがって, 今度は 44 番を探す必要があります。
これは第 16 バイトから始まっています。ストリームが圧縮されているため, 解除すると, 以下のようなものが得られます。
8 0 3 154 <</BleedBox[0 0 516 729]/Contents 13 0 R/CropBox[0 0 516 729]/MediaBox[0 0 516 729]/Parent 3 0 R/Resources 9 0 R/Rotate 0/TrimBox[0 0 516 729]/Type/Page>><</Count 1/Kids[8 0 R]/Type/Pages>>
最初の部分に, どのオブジェクトがどこから始まるかに関する情報があり, そのあとにオブジェクト列挙部分本体があります。8 番のオブジェクトがオブジェクト列挙部分先頭から, 3 番のオブジェクトがその 154 バイトあとから始まることがわかります。
ページ数は 1 ページで, そのページの情報は 8 番のオブジェクトにあります。8 番のオブジェクトは今見たストリームの前半部分で, BleedBox, CropBox, MediaBox, TrimBox はいずれも B5 サイズに指定されています。なぜ同じ値を 4 回も書くのかというと, 意味が同じではないからですが, その話は割愛します。
このページに必要なリソースのデータは, 9 番のオブジェクトを参照する必要があり, またページの中身は 13 番のオブジェクトにあります。前者は圧縮されており, これは 45 番のオブジェクトについたストリームのインデックス 0 にあります。ストリームはファイルの第 324 バイトから始まります。後者は圧縮されていません; ファイルの第 4737 バイトから始まります。
まずは 9 番から:
9 0 <</ColorSpace<</Cs9 12 0 R/DefaultCMYK 12 0 R>>/ExtGState<</GS1 18 0 R/GS2 19 0 R>>/Font<</F1 14 0 R/F2 15 0 R/G1 10 0 R/G2 11 0 R/G3 16 0 R>>/ProcSet[/PDF/Text]>>
フォントの部分を調査しましょう。F1 が 14 番, F2 が 15 番, G1 が 10 番, G2 が 11 番, G3 が 16 番にあり, 合計で 5 つのフォントを含んでいます。これらはすべて 46 番のストリームに圧縮されており, G1, G2, F1, F2, G3 の順番で出現します。
これらのフォントが何者か調べます。まず G1 は DFHSGothicPro6N-W5-Identity-H という名前で, これは DF 平成ゴシック体 Pro6N W5 です。G2 は DFHSMinchoPro6N-W3-Identity-H という名前で, これは DF 平成明朝体 Pro6N W3 です。F1 と F2 は名前からは何のフォントだかわかりませんが, おそらく作ったのでしょう。G3 は HeiMinGaiji-W3-Identity-H ですから平成明朝の外字です。
§1.4 中身を調査
ページの中身 (13 番) を見てみましょう。これは第 4737 バイトから始まるのですが, 中身が <</Filter/FlateDecode/Length 1847>> であり, 実際にはストリームのほうに本体があります。
ストリームの圧縮を解除すると 312 行の長ーい文字列が出てきます。ここに, このページを描画するための指示がすべて入っています。最初に描画するテキストは, ゴシック体のテキストです。
/G1 1 Tf
10 0 0 10 88 545.7001 Tm
.99 Tc
となっていて, フォントサイズは 10 ポイントであることがわかります。さらに, 文字と文字の間に 0.99 の間隔があいていますから, おそらくこれは「注意事項」の部分です。
その次のテキストには,
/G2 1 Tf
8.197 0 0 10 71.4016 525.7001 Tm
とあるので, y 座標がちょうど 20 減少しています。したがって行送りは 20 ポイントです。
次に, 注意事項の 4 番に出てくる がどのようになっているのかも調べましょう。
1.134 w
348.233 397.567 34.134 -16.134 re
線の幅は 1.134, 箱の寸法は横幅 34.134, 高さ 16.134 です。
これで必要な情報はほとんど揃ったので, 再現を始めます。テキストの具体的な位置は, §2 で述べます。
§2. 再現
版面のサイズを B5 にします。フォントは DF 平成明朝 W3 と DF 平成ゴシック W5 を使用します。
§2.1 表紙
表紙から作ります。表紙には以下の項目が記載されます。
- 「試験開始の指示があるまで, この問題冊子の中を見てはいけません」(枠で囲む)
- 科目名(22 ポイント)
- 満点・時間
- 注意事項
- ページ番号(二つ)
まず第一の注意書きを書きましょう。(103, 642.7) の位置から始めます。書いたらそこに枠をつけます。左下は (97.8, 638.52) で, 幅 320.4, 高さ 15.96 です。

このようになります。
次に科目名を大きく書きます。(228, 584.14) に中央揃えです。
満点と時間は, 大きい括弧の中に 2 行に分けて書き,科目名の右側に配置します。右揃えにし, 満点は (433, 598.7), 時間は (433, 578.7) に揃えます。

次に注意事項です。「注意事項」という文字列は, 文字と文字の間隔を 10 ポイントにします。(88, 545.7) から開始します。注意事項の各項目は字下げに注意が必要です。

解答欄のマーク方法の例も表示しましょう。

本家では 10 番の解答欄に 3 をマークした図ですが, ここは乱数で変えるようにしました。
注意事項を列挙したら最後にノンブルです。「1」と中央揃えで (258, 60) に書き, 前後に em-ダッシュを入れます。

右下に管理番号みたいなもう一つのノンブルも入れましょう。

この番号は 10 ポイントではなく 8 ポイントにします。
これで表紙ができました。
§2.2 本文
表紙だけでは物足りないので中身も作りましょう。
ページを改めて, 再び大きな文字で科目名を書きます。(258, 626) に配置し, 中央揃えにします。
科目名の下に, 解答番号が何番から何番までなのかを示します。「解答番号」という文字列の左下は (193, 595) です。

そのあとから設問を並べます。後ろに配点も書きます。

最後に表紙と同じ要領でページ番号をつけます。
§3 今後やること
2 ページの組版再現でしたが, 組版ソフトを用いたわけではありません。実は文字の配置は自動計算ではなく, すべて手でポチポチやっています。

まずはこの位置の計算などを自動化する必要があります。
その次に, 数式にも対応できるように拡張します。
その二つの作業を, 後編に記すことにしましょう。
ということで前編はここまでです。後編がいつ出るかは未定ですが, おそらく冬になると思います。お楽しみに。