前回、flow-mapの概要と、簡単な例、若干極端な利用方法と失敗例を紹介しました。
今回も注意深く使わなければ失敗してしまうという例を紹介します。
2つのregionであるregionA、regionBへ1つのflowを流し込むようなケースです。(というより、ほかの2つはこのパターンよりも複雑なので、利用しようとしたとき「うっかり失敗する」というケースは少ないのではないでしょうか。)
脚注はそれぞれのregionごとに配置される
一見1つのregion-bodyに見えますが、実は上下2つのregion-bodyで構成され、flow-mapでxsl-region-bodyを流し込んでいます。前回見たようにregionのサイズを超えるオブジェクトを挿入すると表示が壊れるかもしれませんし、あまりメリットがないように思えます。このflow-mapがどんなときに有用かというのは今後紹介するかもしれません。
とりあえず上手くいったように見えるFOですが、大きな欠点があります。
脚注をページ下部に配置するとき、正確にはregion-bodyのbottomから脚注の分の領域が確保されます。flow-mapはあくまでマッピングを行うだけで、region-bodyが1つになったわけではありません。つまり次のようなことが起こり得ます。
<fo:flow flow-name="xsl-region-body">
<fo:block >Lorem ipsum dolor sit amet<fo:footnote>
<fo:inline ...>*<axf:footnote-number id="a"/></fo:inline>
<fo:footnote-body>
<fo:block ...>Lorem ipsum ...</fo:block>
</fo:footnote-body>
</fo:footnote>, consectetur ...
</fo:block>
</fo:flow>
上部のregionAの下部に脚注は配置されました。たとえば左右に分けられているregionではそれぞれのregionの下部に表示してほしいでしょうし、当然の挙動ではあります。
複雑な回避方法を考えるよりも、このページシーケンスとflow-mapを使うときに「脚注を使用しない」「図表を使用しない」と制限した方が事故を防げるでしょう。
AH XSL Formatter拡張仕様ではregion-startやregion-endを注の配置に利用可能なので、「脚注ではなく傍注を利用する」といった方法も考えられます。
https://www.antenna.co.jp/AHF/help/ja/ahf-ext.html#axf.footnote-position
flow-mapで幅の違う疑似段組も実現可能だが、段抜きはできない
AH XSL Formatterではregion-bodyのほか、ブロックコンテナに段組を指定可能ですが、段組は通常段の行進行方向の長さが均等になります。
https://www.antenna.co.jp/AHF/help/ja/ahf-fo11.html#fo.block-container
flow-mapを活用することで、段の幅が異なる段組を擬似的に実現可能なのですが、span="all"を指定しても段抜きができないなど、これもまた使い所を考える必要があります。
これらのFOでは、表組などでもうっかり破綻する可能性があるので、くれぐれも注意深く使用しましょう。
参考資料
- 『XSL-FO の基礎 第2版 – XML を組版するためのレイアウト仕様 第2版』(アンテナハウス株式会社, 2017)
- Extensible Stylesheet Language (XSL) Version 1.1 – 6.4.22 fo:flow-map
- JIS X 4179『拡張可能なスタイルシート言語(XSL) 1.1』
前々回:XSL-FO試行錯誤 脚注のインデント
前回:XSL-FO試行錯誤 fo:flow-map 概要編
XSL-FO試行錯誤 fo:flow-map 概要編
今回はflow-mapについてです。今回の記事は「概要編」としてflow-mapとはどんなものなのか紹介します。
region-bodyは複数記述可能
<fo:region-body>は<fo:simple-page-master>に複数記述可能です。region-body同士は重なり得るので、適切に配置します。<fo:flow>の@flow-nameで利用するregion-bodyを指定します。
<fo:layout-master-set>
<fo:simple-page-master master-name="main"
size="JIS-B5 portrait" margin="10mm">
<fo:region-body region-name="en" margin-right="182mm div 2 - 5mm"/>
<fo:region-body region-name="jp" margin-left="182mm div 2 - 5mm"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="main">
<fo:flow xml:lang="ja" flow-name="jp" >
<fo:block ...>日本語</fo:block>
...
</fo:flow>
<fo:flow xml:lang="en" flow-name="en">
<fo:block ...>English</fo:block>
...
</fo:flow>
</fo:page-sequence>
たとえば、翻訳文を併記したいけれどテキストの長さが元の文と異なってしまうときなどにも対応可能ですね。表組や段組で左右の文の位置が揃うよう並べるよりも構造がすっきりしているのではないでしょうか。表組の解除や段抜きなどはないため、図版だけページ中央に表示したいなどの要求があるとややこしくなりますが。
flow-mapについて
本記事の本題はflow-mapです。名前の通りflowのマッピングを行える機能です。簡易な理解ではページシーケンスマスタでのページシーケンスとページマスタの関係を、flowとregionに置き換えたものとなります。ページシーケンスマスタのように条件よる参照分岐はできません。
JIS X 4179『拡張可能なスタイルシート言語(XSL) 1.1』で紹介されているflow-mapの使い方は3パターンほどです。簡単な例としては次のような形があります。
- 別々のregionであるregionA、regionBに対し、同じflowを流し込む
- 1つのregionに対し、別々のflowであるflow1、flow2を流し込む
- regionA、regionBにflow1、flow2を流し込む
この中でもっとも使い方として分かりやすいのは「regionA、regionBに対し同じflowを流し込む」という形でしょう。
<fo:layout-master-set>
<fo:simple-page-master master-name="complex"
size="JIS-B5 portrait" margin="10mm">
<fo:region-body region-name="regionA"
margin-top="1cm" margin-bottom="130mm"
margin-right="182mm div 2 - 5mm"/>
<fo:region-body region-name="regionB"
margin-top="180mm"
margin-left="182mm div 2 - 5mm"/>
</fo:simple-page-master>
...
<fo:flow-map flow-map-name="flowmap">
<fo:flow-assignment>
<fo:flow-source-list>
<fo:flow-name-specifier flow-name-reference="flow-merged"/>
</fo:flow-source-list>
<fo:flow-target-list>
<fo:region-name-specifier
region-name-reference="regionA" />
<fo:region-name-specifier
region-name-reference="regionB"/>
</fo:flow-target-list>
</fo:flow-assignment>
</fo:flow-map>
<fo:layout-master-set>
<fo:page-sequence master-reference="complex"
flow-map-reference="flowmap">
<fo:flow flow-name="flow-merged"/>
<fo:block>...
<fo:block>
&t;/fo:flow>
</fo:page-sequence>
<fo:flow-map>に@flow-map-nameを指定し、<fo:page-sequence>でそのflow-mapを参照するようにします。
流し込まれるソースのflowの@flow-nameと同じ値を<fo:flow-source-list>の子、 <fo:flow-name-specifier>の@flow-name-referenceに、流し込み先のregionを<fo:flow-target-list>の子、<fo:region-name-specifierの@region-name-referenceに指定します。今回はregion2つに対してflow1つを割り当てました。
<fo:page-sequence>に@flow-map-referenceで参照するflow-mapを指定する必要があります。
複数flow-mapを用意すれば、同じflow-nameのflowに対し、flow-mapの参照を変更することで別のregionに流し込むことも可能です。
さらに細かいregionとflow-mapを利用することで、次のようなレイアウトも可能です*。
試行錯誤であるところはここからで、このregionはマッピングされているとはいえそれぞれ独立です。たとえばフォントサイズを大きくするとテキストの位置がずれ、次の行のテキストと重なるかもしれません。
- * このようなレイアウトを実現するほかの方法として
<fo:block>に都度インデントを指定するような方法も考えられます。そのときはテキストの内容を行ごとにあらかじめ分割しておく必要があるでしょう。
上は極端な例ですが、領域が独立していて困ることはほかにもあります。この話題については来週に続きます。
参考資料
- 『XSL-FO の基礎 第2版 – XML を組版するためのレイアウト仕様 第2版』(アンテナハウス株式会社, 2017)
- Extensible Stylesheet Language (XSL) Version 1.1 – 6.4.22 fo:flow-map
- JIS X 4179『拡張可能なスタイルシート言語(XSL) 1.1』
前回:XSL-FO試行錯誤 脚注のインデント
XSL-FO試行錯誤 脚注のインデント
さて、筆者は入社してからXSL*1を学び始め、1年と少しを超えたくらいになりました。社内のプロフェッショナルの背は未だ遠く、学びの日々です。
XSLについての日本語情報の記事は母数が少ないこともあり、学ぶなかで、思ったように組めなかったり意外な発見があったりといった知見を記事として何回か連載しようと思います。
記述については正確さを心掛けますが、誤りがありましたらご指摘ください。
また、本記事中のコードなどについて保証をするものではありません。ご了承ください。
出力にはAntenna House XSL Formatter V7.1を利用しています。
今回は脚注のインデントについてです。
脚注のインデント
fo:footnoteとfo:floatは行外オブジェクトという種類のFOである。本文の流れから外れた位置に配置するのが特徴である。
脚注の文、オブジェクトの本体は行外に置かれますが、そのオブジェクト配置の基準位置(アンカ)は<fo:footnote>を記述した箇所になります。この部分に脚注の文と対応を示す記号や番号(合印)を置き、どの脚注がどの文に対するものであるかを示します。
傍注(または縦組での脚注)などで注の位置と注の文が近い距離にあるために省略する場合もあります。
この脚注の文の本体、合印の参照からはじまる箇所は<fo:footnote-body>の子として記述します。脚注文の書き方の構造はたとえば
*2にあるような記述になります。
<fo:block>Lorem ipsum... elit<fo:footnote>
<fo:inline baseline-shift="super" font-size="small"><fo:inline font-family="fantasy">*</fo:inline><axf:footnote-number id="nine"/>
</fo:inline>
<fo:footnote-body>
<fo:block line-height="1.2" font-size="small"
start-indent="2em" text-indent="-2em">
<fo:inline space-end="1.2em">
<fo:inline-container>
<fo:block>
<fo:inline font-family="fantasy">*</fo:inline><axf:footnote-number-citation ref-id="nine"/>
</fo:block>
</fo:inline-container>
</fo:inline>Lorem ipsum ... laborum.
</fo:block>
</fo:footnote-body>
</fo:footnote>, sed ...
ブロック全体のインデントをstart-indentで指定し、1行目のインデントを制御するtext-indentに負の値を指定することで突き出しを実現します。
上のFOは脚注番号のテキストが取る幅が変化しなければ綺麗に表示されますが、脚注番号の桁数が増え、脚注の文が2行以上になるときに残念な結果になってしまいます。
さて、原因は増えた脚注番号の幅です。では、これを解決する方法を考えましょう。
脚注番号の改良
方針は3つほどあります。あるいはこれらを組み合わせても良いでしょう。
脚注番号の書式を変更する
XSLT、またはaxf:footnote-number-format*3への指定によってパディングを行い、桁数による脚注番号幅の揺れを抑えます。「*1」の表示が「*01」や「* 1」のようになるので少し悩ましいですね。
増分の幅を確保しておく
先程のコードブロックではインラインコンテナで脚注番号の箇所を囲った上でspace-endを指定したインラインの子にしていました。
インラインコンテナに幅を指定し、space-endを調整し脚注の文の開始位置を固定します。
<fo:footnote-body>
<fo:block ... start-indent="2em" text-indent="-2em"><fo:inline space-end="0.5em">
<fo:inline-container inline-progression-dimension="1.5em" >
<fo:block>
<fo:inline ... >*</fo:inline><axf:footnote-number-citation ref-id="nine"/>
</fo:block>
</fo:inline-container>
</fo:inline>Lorem ipsum...
先ほどの例から、インラインコンテナの幅指定とspace-endの調整だけで目的が達成できました。
インラインコンテナ以下でtext-alignを指定し、インラインコンテナの幅内で右寄せにすることなども可能です。
リストブロックにする
より強く脚注番号と脚注の文の分離を志向するのであれば、インデントによる制御ではなく、リストブロックのアイテムラベルに脚注番号、アイテムボディに脚注の文を入れる方法などがあります。この方法の場合、記述がかなり増えます。
*1 The Extensible Stylesheet Language Family (XSL)
*2 『XSL-FO の基礎 第2版 - XML を組版するためのレイアウト仕様』(アンテナハウス株式会社, 2017年3月)
*3 AH Formatter V7.1 マニュアル| axf:footnote-number-format
MathMLで空欄付きの数式を表現する
センター、もとい、大学入学共通テストがおこなわれましたね。
数学や物理では数式に空欄が入った問題がおなじみです。「空欄アに入る数を答えよ」といったものですね。
ふと、「これをMathMLだけで組めるのだろうか」と思い、軽く調べてみました。
文字自体は<mtext>で良いとして、空欄であることを示すための枠線が必要です。
borderのようなattributeを持つ要素を探してみると、<mtable>が該当しました。
frame="solid"のようにして枠線が付けられます。枠で囲った「ア」を描画してみました。
セルや行、列単位で枠線を引くのであればcolumnlinesなど細かいattributeを指定しますが、今はもっとも外側の枠線だけて十分ですね。
<mtable frame="solid"><mtr><mtd><mtext>ア</mtext></mtd></mtr></mtable>
数式中に記述し、AH Formatterで出力してみます。
根号の中に空欄が表示されています。<mtable>はwidthを指定できるので、幅を明示的に指定しました。
<mtable>は数ベクトルや行列を表現するために用いるのが主用途であることを考えると、電子的に配布を考える場合は別途注釈や内容のMathMLを記述した方が良いかもしれません。ともあれ、空欄付きの数式を表示してみました。
より簡潔な方法もありました。<menclose>です。
こちらは「ア」の前後のパディングに、とりあえず<mspace>を使用しています。
ふと思いたち書いてみた方法なので、あるいはもっと適した方法があるのかもしれません。
なお、本記事で記述した方法を推奨したり保証するものではありません。
参考文献
Mathematical Markup Language (MathML) Version 3.0 2nd Edition
XSL-FOでスライドを作成してみる
先日社内でプレゼンの機会がありました。プレゼンといえばスライドですね。プレゼンには身振り手振りやデモなどもありますが、制作期間や発表時間といった制限時間、不測の事態や資料の提出などを考えるとスライドは安定性が違います。欲をいえばプレゼン時にスクリーンに出す資料と頒布する資料は対応を取りつつ情報量を変えたいなどもありますが、こういったものはリソースとの戦いですね。
スライドといえばMicrosoft PowerPoint、macOSであればKeynoteもあります。
これらのソフトを立ち上げスライドのファイルを読み込みいざオンラインプレゼン、オンライン会議ツールを立ち上げ……あ、画面が固まった。なんてことも。GUIによるプレゼンテーション作成ソフトと、オンライン会議ツールを同時起動しているゆえに負荷がそこそこ高いからでしょうか。
他の手段として、HTML+CSS+JavaScriptスライドというのもあります。Webブラウザがあれば動くということで、まあ悪くありません。クラウドのオフィスツールではGUIでこれらを作ることもできます。外部サービスでは会社で採用していないと使える機会が限られるのが難点でしょうか。
これらのスライドを、PDFに別で保存することがあります。PDFであれば、会議の進行役に念のため予備を渡しておいて「表示環境がない」ということもそうありません。先に挙げたようなオフィスツールでPDF出力をしてもよいし、作り方にもよりますがHTMLスライドもWebブラウザなどからPDFとして出力できます。
今回なぜか発表本番2、3日前にXSL-FOを直接書いてスライドをPDF出力していました。せっかくなのでそのときのFOを一部ご紹介します。
スライドを作成するとき、コンテンツはスライドのコンテンツとして都度作成しなければなりません。構成や情報量は、発表時間やオーディエンス、発表環境などによって、練り直さなければならないためです。「スライドの一部を書き換えて何度も利用する」というのはよくあるかもしれませんが、媒体の方向性としては一品ものと捉えてもよいでしょう。1からスライドを作るのにXSL-FOを直接書くというのは個人的におススメできないことはあらかじめお伝えしておきます。
テキスト内容などは変更しています。また動作などについて保証するものではありません。使用したのはAntenna House XSL Formatter V7.0 MR5となります。
ページレイアウトと背景
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:axf="http://www.antennahouse.com/names/XSL/Extensions"
font-size="28pt"
line-height="1.8"
font-family="sans-serif"
color="white">
<fo:layout-master-set>
<fo:simple-page-master master-name="slide"
background-image="linear-gradient(to left, rgba(0,0,0, .7) 0%,rgba(0,0,0, .9) 100%) )"
page-width="1920px" page-height="1080px"
margin="0"
>
<fo:region-before extent="2cm" />
<fo:region-after region-name="footer" extent="2cm"/>
<fo:region-body region-name="body" margin="2cm 3cm" />
</fo:simple-page-master>
</fo:layout-master-set>
ページサイズは幅1920px、高さ1080pxのFHDサイズにしました。これはPDF出力時に物理的なサイズに変換されます。
ルートでフォントサイズ、行の高さ、フォントファミリ―を指定しています。文字色も指定していますが、背景の色と関連するのでルートよりも後で指定する方が望ましいかもしれません。その背景は単純ページマスターで指定しています。線形グラデーションで灰色の度合いを変化させています。rgbaで透過色を指定をしているのは当初この下に背景画像を置こうとしていたからです。実際に追加するのであれば、linear-gradientの後に指定します。
ヘッダーとフッターの領域を2cmずつ用意し、本文区画は上下2cm、左右3㎝のマージンをとっています。
タイトルページ
<fo:page-sequence master-reference="slide">
<fo:flow flow-name="body">
<fo:block-container id="title"
<fo:wrapper font-size="92pt" line-height="2" text-align="center">
<fo:block margin-top="2cm" >
なんかものすごい<fo:block />プレゼン</fo:block>
</fo:wrapper>
<fo:block-container space-before="3cm"
text-align="end">
<fo:block>2020-11-30</fo:block>
<fo:block>アンテナ太郎</fo:block>
</fo:block-container>
</fo:block-container>
</fo:flow>
</fo:page-sequence>
タイトルページではヘッダーとフッターを表示しないよう、その後のページとページシーケンスを分けていますが、参照しているページマスターは同じです。ページマスターを分けるほど複雑な構成は必要なかったためこのようにしています。
タイトルは中央揃え、名前や日時はendに揃えています。よりこだわるなら名前や日時のブロック自体は右側に置いた上で、ブロックの内側では左揃えといった置き方もできます。
ヘッダーとフッター
ヘッダーに社のロゴ、フッターにスライドのページ番号を表示することにします。
<fo:page-sequence master-reference="slide" initial-page-number="2">
<fo:static-content flow-name="header" >
<fo:block-container><fo:block text-align="end">
<fo:external-graphic src="url(https://www.antenna.co.jp/img/ah_headerlogo.svg)" width="8cm" height="1lh"/>
</fo:block>
</fo:block-container>
</fo:static-content>
<fo:static-content flow-name="footer">
<fo:block-container>
<fo:block
text-align="end" space-before="1cm" end-indent="2cm">
<fo:inline font-family="serif"> p-<fo:page-number font-variant="oldstyle-nums"/> </fo:inline>
</fo:block>
</fo:block-container>
</fo:static-content>
...
<fo:page-sequence>
ヘッダーのロゴの高さを行の高さにしました。このスライドでは使用していませんが、スライドの見出しをマーカーで参照し、ヘッダーで表示することを想定したサイズ指定です。SVGのロゴ画像なのでサイズ変更が容易で助かりました。
本文がサンセリフなのに対し、フッターのページ番号はセリフにすることで本文と区別できるようにしています。ついでにページ番号の数字はオールドスタイルにしてみました。ページ番号の前に接頭辞をつけるにはページシーケンスでfo:folio-prefixをつける方法がありますが、
ページ番号参照のときに「p-4」のように表示させたいわけではないので、通常のテキストとして配置しています。
initial-page-number=”auto”では前のページ番号を引き継ぐので、上で記述されているページの開始番号のように指定しなくとも問題ありません。
箇条書き
スライドでは定番の箇条書き表現。GUIのプレゼンテーションツールやHTMLとXSL-FOでかなり書き心地が異なります。
<fo:block-container break-before="page">
<fo:wrapper font-size="62pt" font-weight="bold">
<fo:block>
XSL-FOの関連仕様
</fo:block>
</fo:wrapper>
<fo:list-block provisional-distance-between-starts="0.5em"
provisional-label-separation="0em"
>
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block xml:lang="en">•</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block>Extensible Markup Language
</fo:block>
</fo:list-item-body>
</fo:list-item>
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block>•</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block>XMLNamespace</fo:block>
</fo:list-item-body>
</fo:list-item>
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block>•</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block>XML Transformations</fo:block>
</fo:list-item-body>
</fo:list-item>
</fo:list-block>
<fo:block>
</fo:block>
</fo:block-container>
1つの箇条書き項目を表すために記述がリストブロック、リストアイテム、リストアイテムラベル、リストアイテムボディの要素と、ラベルとボディ間の間隔を指定する必要があります。入れ子のリストブロックや長さがバラバラのリストアイテムなどがあるとき真価を発揮する箇条書き用の構造ですが、これくらいの内容であれば項目ごとにブロックで記述した方が簡単です。
ソースコードを複数段表示する
スライドはスクリーンなどの都合もあり幅の方を長くとることが大半であると思います。これは改行で整えられた行数の多いコードを表示するのにあまり向きません。ブロックコンテナ―に段組を指定し、スライド1枚に詰め込めるソースコードを増やしてみました。
<fo:block-container break-before="page" column-count="2"
border="0.3pt solid white"
axf:column-gap="2em" axf:column-fill="auto"
>
<fo:block border-bottom="0.3pt dashed white">XSL</fo:block>
<fo:block
font-size="24pt"
line-height="1.2"
axf:line-number="show"
axf:line-number-color="white"
axf:line-number-font-size="20pt"
axf:line-number-offset="10pt"
axf:line-number-font-style="italic"
margin="2pt"
white-space-collapse="false" white-space-treatment="preserve"
linefeed-treatment="preserve"
xml:space="preserve" font-family="monospace">
..</fo:block>
</fo:block-container>
ということでXSL-FOで直接作成したスライドとその記述の一部を紹介しました。今回のように直接書くのはやはりおススメできませんが、他のXML文書などからスライドを用意するのであればなかなか悪くないのでは、と感じました。
fo:bookmarkというマークアップから考えるしおり
『岩波国語辞典第八版』*1によれば、「しおり(しをり)」には2つの意味があります。
1. 本の読みかけのところに挟んで目印とするもの 2. 初めての人などにわかりよく説明した本。手引き
岩波国語辞典第八版
「目次」は次のように記されています。
(書物の)内容の題目を、書かれている順に並べて記したもの
岩波国語辞典第八版
しおりについて、1の意味では、読み手側が設定するものという印象が強いですね。ちなみに岩波国語辞典だとブックマークはWebブラウザのブックマーク(お気に入り)機能についての言及となっています。
さて、XML組版の仕様であるXSL1.1仕様にはしおりを作成するための機能fo:bookmark、fo:bookmark-title、fo:bookmark-treeが存在します。1.1の仕様に1.0との差異が記載されているのですぐにわかりますが、実は1.0のときには存在しなかった機能です*2。
『XSL-FOの基礎 第2版』*3にしおりのFOについての解説があります。また、Antenna House XSL Formatterの*4サンプルFOのページ*5からしおりの設定されたPDFを確認できます。
<fo:layout-master-set>
…略…
</fo:layout-master-set>
<fo:bookmark-tree>
<fo:bookmark internal-destination="MARK001">
<fo:bookmark-title font-weight="bold">
目次</fo:bookmark-title>
</fo:bookmark>
<fo:bookmark internal-destination="MARK002"
starting-state="hide">
<fo:bookmark-title>第1章</fo:bookmark-title>
<fo:bookmark internal-destination="MARK003">
<fo:bookmark-title>1.1</fo:bookmark-title>
</fo:bookmark>
...
</fo:bookmark-tree>
...
<fo:page-sequence ...>
<fo:block font-size="2em" font-weight="bold" space-after="5mm" id="MARK001">Bookmark sample-1</fo:block>
<fo:block font-size="1.5em" font-weight="bold" id="MARK002">Lorem Ipsum</fo:block>
<fo:block space-before="4mm" margin-left="2em" line-height="1.4">
<fo:block keep-with-next="always" font-weight="bold" id="MARK003">Lorem ipsum dolor sit amet</fo:block>
<fo:block>...</fo:block>
</fo:block>
...
</fo:page-sequence>
ごく簡単に説明すると、fo:rootの子としてしおりのツリーを用意し、その子として、目的のIDやURLなどをプロパティの値に持つしおりfo:bookmarkとしおりの項目名fo:bookmark-titleを記述します。上の例ではfo:blockにそのIDが記述されていますね。ツリー構造ですので、階層も表現できます。
PDFなど、ページドメディアへの出力を意識したマークアップであるXSL-FOの機能として用意されていることから分かる通り、これは文書コンテンツ提供側が用意するしおりです。
XSL-FOは(中間形式にせよ)ページドメディアとしてかなり明示的なマークアップを求めますから「fo:bookmark*」は「この箇所をしおりとする」というはっきりとした意思表示となります。
文書コンテンツ作成者がコンテンツ頒布時に「この部分は読みかけ」というしおりを作ることは稀なことと捉えて良いでしょう。そして、fo:bookmark-titleというマークアップ名から分かる通り、しおりの項目名は長い段落などを記述する箇所でもありません。
目次項目は当然その項の内容を反映しまとめたものになりますから、しおりは多くの場合目次項目と同じ箇所でマークアップされ、対応したPDFビューアーでは「しおりを表示」といった機能でページ表示とは独立して表示できます。
特に数百、数千ページのPDF文書を頻繁に読む機会のある方は頷かれるのではないかと思いますが、紙の文書ではそれこそ「しおり」を挟んで分厚いページの束をまとめてむんずと掴んで目次に戻ったりできますが、PDFビューアー上で毎回目次ページへ戻って目次項目を確認するというのはそこそこ時間がかかります。
個人的には、しおりの重要な点として「ページ外の要素」であることが大きいのではないか、と考えています。目次で「2.3 詳細 200ページ」、文章中に「299ページに詳細」、索引ページに「fo:bookmark…300p」といったページ参照があるのはそれはそれで重要ですが、「本文を読みながら文書(あるいは文書外)の他の箇所への目印へすぐにアクセスできる」機能がしおりといえるのではないでしょうか。そして、XSLでは文書コンテンツ提供側がそれを提供することになります。
ところで、XSL-FOとXSL-FOプロセッサーによる処理ではPDFの作成時にしおりをマークアップすることになるので、すでにPDFとなっているものにしおりを付与することは範囲外です。Antenna House Formatterでは「PDFを画像のように埋め込む」といったことができるので、多少トリッキーな方法で後からしおりを付与することもできなくはありません。しかし専用のソフトウェアがあればもっと分かりやすく、そして効率的にしおりを付与できます。
隔週で火曜に開催している「ちょっと一息 アンテナハウスウェビナー」、11月10日(火)16時からは「そもそも、PDFのしおりとはなにか ~目次と何が違うのか。どう作って活用するか~ 」というテーマでお送りします。この記事を書いているのが担当者でないため、発表内容の詳細はわかりません。しおりってなんなんでしょうね……。この謎を明かすためにも明日のウェビナーは必見です。
参考文献・資料
- *1 『岩波国語辞典第八版』(岩波書店, 2019年11月22日第1刷発行, ISBN 978-4-00-080048-8)
- *2 https://www.w3.org/TR/xsl11/#change10
- *3 https://www.antenna.co.jp/AHF/ahf_publication/data/xsl-fo-v2/201603282131.html『XSL-FO の基礎 第2版 – XML を組版するためのレイアウト仕様』(アンテナハウス株式会社, 2017年3月, ISBN 978-4-900552-48-7)
- *4 Antenna House Formatter
- *5 https://www.antenna.co.jp/AHF/ahf_samples/sample-fo.html#pdf
- PDF資料室:PDFのしおりってなに? どうやって作るの?
- PDF資料室:PDFしおりの活用術―しおりの仕組みと活用法―
LwDITAで(途中まで)ドキュメントを書いてみました
これまでに数回Lightweight DITA(LwDITA)に言及したことがありました。LwDITAやDITAについては記事末尾の関連記事、参考資料をご覧ください。ざっくり書くと、リッチな文書フォーマットとその簡略化版です。
最近、社内でドキュメントにLwDITAを試用して執筆しています。これは少し正確ではなく、より正確にお伝えするなら「執筆していた」となります。現在DITA(ただし一部機能しか使用しない)に移行中となります。
そこそこ文量のあるドキュメントに使ってみての利点、欠点について記そうと思います。一部、実装と仕様どちらに対しての言及であるのか不明瞭である点がありますがご容赦ください。
LwDITAでできること
DITAとしての利点と、LwDITAとしての利点があります。DITAとしての利点はおおよそ次のようになります。
- DITA文書の一部としてDITA-OTで処理可能
- conref、keyrefが利用できる
- トピック単位でファイルを分けて管理できる
- メタデータに索引用のキーワードを集約できる
LwDITAはDITA文書の一部として、通常のDITAと混在して処理が可能です。管理の観点からすると必ずしも良くはありませんが、「最初LwDITAで書いてDITAへ移行していく」今回の私のユースケースや、「既存資産のDITAやXML形式を利用しつつ新規記述のコストを減らす」といったときに有用です。
conrefやkeyrefはDITAの機能で、簡単に言えばプログラム言語における変数をドキュメント中に使用できます。専門語の揺れを防いだり、URLの変更などに対応できます。長期的に同じドキュメントを使用したいときに便利です。
「トピック単位でファイルを分割して管理できる」、DITAがトピック指向の設計であるので、条件が合致するときは各ドキュメントの構成のシンプルさを保つことができます。後述する「索引の半自動化」に係わるところです。「メタデータに索引用のキーワードを集約できる」というのも大体同じメリットですが、メタデータを書く箇所がフォーマット側で各ファイル単位に用意されているものは実はあまり多くありません。
そして1行では書きにくいメリットとして、構造のどの位置で呼ばれたかによって、適用されるセクションレベルを変更できるという恩恵があります。簡単に例を挙げると、h2レベルの内容の途中で呼ばれた別ファイルの見出しトップレベルはh3と解釈されます。書いているうちにトピックを分割したくなったときなどに有用です。
LwDITAとしてDITAに対する利点は次があります。
- 単純な最低限のマークアップ(em、strong、italic、リスト……)
メディアコンテンツ対応も地味に仕様としてはDITA1.3より進んでいるのですが、これは出た時期によるもので、実用的にはDITAでも対応されているはず。
さて、とくにMDITA(と一部拡張プロファイルとしてHDITA記法)を利用していたのですが、次の利点があります。
- MDITAではYAMLフロントマターにkeyword、category、author、source、トピックIDを記述可能
- DITAに限らず別形式へ変換可能という安心感
メタデータは単純なKeyValue、あるいは簡単な入れ子となるようなものが多いため、汎用エディタを使ってXML形式で記述するのは冗長に感じるときがあります。YAMLでとりあえずパパっと書けるのは楽でした。
最終形をPDFと考えたとき、DITAではやりづらくなる箇所もあるかもということで、別形式にしやすい意味ではMarkdownは安心感があります。その分表現力に制約がかかりますが。
「チームメンバーの記述可能なフォーマットの共通スキルがHTMLやMarkdown」といったケースもありうるかもしれません。
LwDITAでできないこと
- 対応しているDITAメタデータ(prolog)が少ない
- 対応しているプロパティ、要素が少ない(特にMDITA)
- 独自拡張のDITAが対応できない
さもありなんといったところで、作業が進むと、簡易形式ゆえにオミットされた機能が使いたくなってきます。上に挙げた理由で、今はDITA化を進行中です。とはいえ、変換後もLwDITAと対応できない要素はあまり使っていません。
とくにMDITAの不満点として、<note> に対応する記法がありません。以前あった記法もなぜかdepricatedになったので、注やtipsなど、荒涼としたドキュメントにおけるポップでおしゃれなアクセントが書けないのです。
XDITAを飛び越えてフルウエイトのDITAに変換すると、<indexterm>、索引語の指定が使えるようになります。keywordとしてYAMLフロントマターに記述していた語へこのマークアップを行うと、トピックの開始位置に索引が自動でつくように設定できます。入れ子にもできるので索引のサブ項目もほとんど労力をかけずに可能です。
「特殊化したDITA」について軽く触れましたが、AH-DITAという拡張ではfo:propプロパティによってXSL-FO語彙によるスタイル付けをダイレクトに行うことができます。また、図表のフロート配置も<floatfig>で、より自由に行えるようになります。
その他細かいところや全体像などについても、そのうち記事にできればと思います。
参考資料・関連記事
- MDITA(LwDITA uses Markdown)の書き方
- XMLエディタで始めるリッチなMarkdown入門?
- DITAとしてのMDITA
- Lightweight DITA: An Introduction Version 1.0
- DITA 超入門
W3C「日本語組版の処理要件」の最新版が8月11日に公開されました
日本語組版の処理要件(Requirements for Japanese Text Layout、略称 JLREQ)のWorking Group Note最新版が2020年8月11日に公開されました[1]。page2020のXMLパブリッシング交流会『ウェブ出版と日本語組版の未来』で言及されていたものですね[2]。
日本語組版におけるさまざまな事柄について触れられており、項目も多いので「最初から最後まで順に読む」というよりは「必要な箇所を探して都度読む」という読まれ方が多いかもしれません。
この記事を書いている人間はそうで、今まで序論などはあまりしっかり読んでいなかったのですが、この機会に一読することにしました。
序論の「この文書の目的」に次のようにあります。
すべての文化集団は,独自の言語,文字,書記システムを持つ.それゆえ,個々の書記システムをサイバースペースに移転することは,文化的資産の継承という意味で,情報通信技術にとって非常に重要な責務といえよう.
この責務を実現するための基礎的な作業として,この文書では,日本語という書記システムにおける組版上の問題点をまとめた.具体的な解決策を提示することではなく,要望事項の説明をすることにした.それは,実装レベルの問題を考える前提条件をまず明確にすることが重要であると考えたからである.
また、「この文書の執筆方針」には次のようにあります。
組版処理と読みやすい組版設計の関係は別問題である.しかし,両者は不可分の関係があり,解説でも両者を同時に説明する事項も出てくる.しかし,できるだけ両者を区別して記述することを心掛けた.具体的な方法としては,読みやすい組版設計の解説は,できるだけ注記で述べるようにした.
しばしば「Rule」あるいは「Specification」であるかのように捉えられることもありますが、
「Requirements」、それも(主には)組版処理への要件といえます。
さて、「J. Revision Log」に以前の版からの変更点が簡潔に記されています。
ルビについての記載の集約他、表現の修正、閲覧性の向上などが挙げられていますね。
Merged and simplified handling of overhanging ruby text on punctuation marks (former 3.3.8 f,g,h).
約物にかかるルビ文字の定義を集約し簡単化しました (以前の 3.3.8 f,g,h).
[Issue, PR]English wording fixed.
英語表現の修正.
[Issue, PR; Issue, PR; Issue, PR]Merged English and Japanese documents into a single document, with switches to allow readers to view the text in either language, or both.
英語版と日本語版の統合,および言語切替機能の導入.
[7dcc927; Issue, PR]Link targets were assigned to each list item and note, making it possible to point into the document in a more fine-grained way.
文書内リンクがリスト要素とノートにつくようになり,文書内の特定の場所を指すリンクが利用できるようになりました.
Also minor editorial changes were made to structure and wording of the text. A detailed list of changes, including diffs, can be found in the github commit log.
また細かな構成・表記上の修正を行いました.変更点を含む詳細な変更履歴については github のコミットログを参照してください.
「以前の3.3.8 f,g,h」、つまりルビが親文字よりはみ出した場合の処理の調整で、ルビ後に一部の約物が来るようなケースですね。
1版の該当箇所をみると、確かにほぼ同じ文で、対象にしている文字が異なるものの、その調整処理も同じにみえます[3]。
Webページ上の話になりますが、英語版と日本語版を並列表示可能になったことによって、表現が適切であるか閲覧者が比較しやすくなったといえるでしょう。
煩わしければ片方だけにもできますし、データ量としては英語版の文章、日本語版の文章両方を合わせたところで大きいとは言えないはずです。
普段はあまり意識しないのですが、W3Cのページレイアウトも2012年のJLREQのそれと比べ、改良されています。画面が横に広いと目次がサイドバーについて行が画面端から画面端に伸びるのを防いでいたり、
Noteなどの表示スタイルが枠と背景色で見やすくなっていたりしていますね。
具体的にどのような変更があったかですが、コミットログを少しだけ見てみると次のようなコミットなどがありました。
- 「processed」「processing is」などとしていた箇所をそれぞれ具体化
- – it is processed identically to mono-ruby;
+ it is laid out identically to mono-ruby;
- – it is processed identically to mono-ruby;
- ルビの項での「letter」を「character」に修正する
- 画像サイズを削減
JLREQについてのGitHubのissue[4]でOpenのものは2020年8月17日時点で63ありました。
内容とは別のissueもありますが、「question」「enhancement」「future」などのラベルから気になるトピックを見てみると良いでしょう。
以前のイベント参加レポート[2]でも言及しましたが、これは公開された活動です[4]。
英語での意見投稿はハードルが高いと感じている方も、日本語で投稿した内容をJLREQのチームが拾い上げ、翻訳を行ってくださいます。
過去・現在の書籍組版などからの要件もありますが、Webをはじめさまざまなメディアがこれからも変化を続けるにしたがい、「日本語組版の処理要件」自体も変化するでしょう。
これが継続的な活動として続けられていることに改めて感謝の気持ちを抱きました。
- [1] Requirements for Japanese Text Layout
日本語組版処理の要件(日本語版)
W3C Working Group Note 11 August 2020 https://www.w3.org/TR/jlreq/ - [2] 「2/7のXMLパブリッシング交流会に参加してきました」- アンテナハウス I Love Software!2
- [3] https://www.w3.org/TR/2012/NOTE-jlreq-20120403/ja/#adjustments_of_ruby_with_length_longer_than_that_of_the_base_characters
- [4] https://github.com/w3c/jlreq/issues/
海外ウェビナー動画「CSSとXSL-FO:印刷出版に私はどちらを使うべきでしょうか?」(日本語字幕付き)
Xplor International(https://xplor.org/)主催のウェビナーにて、弊社副代表のMichael Millerが発表を行いました。
タイトルは「CSSとXSL-FO:印刷出版に私はどちらを使うべきでしょうか?」(原題:CSS or XSL-FO: Which should I use for producing print publications)です。
「CSSとXSL-FO、どちらを使うべきでしょうか?」という質問をよくいただきます。こののウェビナーではCSSとXSL-FO、それぞれの特徴や歴史的な背景を昨今の出版を取り巻く環境も含めてご紹介しています。技術的な内容は少なめで、どなたでも理解しやすい内容となっています。
ウェビナーの様子はYouTubeでご覧いただくことができますので、ぜひご視聴ください。
なおYouTubeの字幕機能を使えば、日本語字幕付きでご視聴いただくこともできます。
字幕の設定方法は、
- 動画右下の設定アイコン(歯車マーク)をクリック
- 「字幕」をクリック
- 「日本語」にチェックを入れる
以上の設定で日本語字幕付きでご視聴いただけます。

XSL-FO/CSSスタイルシートにより、XMLとHTMLをサーバー上で高速にPDFに変換!

アンテナハウス ウェビナー スケジュール一覧 2020
CommonMark文書をXSL-FO経由でPDFへ変換する
前回、cmark[1]でCommonMark[2]のASTをXML形式で得られることを確認しました。
記事中で登場するコードの動作についての保証はできかねます*1。ご了承ください。
今回の記事で使用した環境は次になります。
- Windows 10 Home 1909
- MSXSL V6.0
- cmark 0.29.0
- Antenna House XSL Formatter V7.0 MR3
文書はその目的に適した構造を持ち、組版では多くの場合その構造に沿ってマークアップを行うでしょう[3]。
Markdownはそれらを放棄しているために、文書として組版するためには不足するものを補う必要があります*2。基本的には、「レポート」であるならレポートの見た目になるよう、Markdown文書とレポートの対応関係を用意する必要があるということです。例えば「<heading level="1">のテキストをレポートのタイトルとする」として、「レポートタイトルはフォントサイズは本文の2倍、中央寄せ、前後のアキは……」とスタイルを用意していきます。書籍を組むのであれば目次や索引などが補う対象となります。
記法の数が比較的少ないCommonMarkとはいえ、すべての対応関係について記述するのは労力が大きいため、一部のみとします。また、前回言及した「Markdown + XSL → PDF」[4]の記事ではリンク記法を応用して脚注記法を用意していますが、処理が複雑化しますのでこういった応用もしません。
変換するCommonMark文書
# CommonMark文書をAH XSL FormatterでPDFにする
## はじめに
[XSL](https://www.w3.org/TR/xsl/)は2部に分けられます。一方はスタイルシート言語としてのXML語彙(XSL-FO)、他方はXML変換のための言語XSLTです。
## 基本版面
`<fo:simple-page-master>`とその子要素に基本版面のサイズとそれぞれの区画を記述していきます。
* `<fo:region-before>`
* `<fo:region-after>`
* `<fo:region-start>`
* `<fo:region-end>`
* `<fo:region-body>`
startからendは*行進行方向*、beforeからafterは*ブロック進行方向*です。
## 本文
```xml
<fo:flow flow-name="xsl-region-body">
<fo:block>
...
```
region-bodyの`region-name`の既定値は「xsl-region-body」ですが、**変更**できます。
基本版面が完成したら、ヘッダやフッタなど配置が固定的なものを配置する`<fo:static-content>`と、
見出しや本文の`<fo:flow>`を記述していきます。版面で指定したページマスタや区画を指定します。
ようやく紙面に文字を表示するための記述に入れます。LaTeXなどではクラスファイルとしてまとめられている箇所を自力で書いているようなものですので、慣れるまでは煩雑に見えるかもしれません。
XMLへの変換
このCommonMark文書をPDFに変換します。
cmarkによるCommonMark文書のXML変換はコマンドライン操作になります。標準出力に出力されるため、出力結果をファイルに書き込んでいます。変換結果のXMLで文字化けがしていたら、コマンドラインの文字コード設定などが関係しているかもしれません。
> cmark.exe doc.md -t XML > result.xml
このXMLをXSLTで変換し、得られたFOをAntenna House XSL FormatterでPDFへと変換します。先に結果を見てみましょう。
PDF出力結果


源ノ明朝、源ノ角ゴシックを使用したPDFが出力されています。欧文はTimes New RomanとDeja Vu Sansです。
XSLTの抜粋
AH Formatterではソフトウェア側の設定をあまり弄らずともOpenTypeフォントを使用できます。
<!-- XSLT -->
<xsl:attribute-set name="mainfont">
<xsl:attribute name="font-family">Times New Roman, 源ノ明朝</xsl:attribute>
<xsl:attribute name="font-size">14q</xsl:attribute>
<xsl:attribute name="line-height">1.7</xsl:attribute>
</xsl:attribute-set>
<xsl:template match="/">に<fo:root>を置きます。
<xsl:template match="/">
<xsl:variable name="varAuthor" select="'アンテナハウス'" />
<xsl:variable name="varTitle"><xsl:text>CommonMarkからXSL-FOでPDFを作る</xsl:text>
</xsl:variable>
<xsl:variable name="varMainPageName" select="'main'" />
<fo:root xsl:use-attribute-sets="attsRoot">
<!-- 基本版面 -->
<xsl:call-template name="layoutMasterSet">
<xsl:with-param name="mainPageName" select="$varMainPageName" />
</xsl:call-template>
<!-- 表紙 -->
<xsl:call-template name="coverPage">
<xsl:with-param name="mainPageName" select="$varMainPageName" />
<xsl:with-param name="author" select="$varAuthor"/>
<xsl:with-param name="title" select="$varTitle"/>
</xsl:call-template>
<!-- 文書内容 -->
<fo:page-sequence master-reference="{$varMainPageName}"
xsl:use-attribute-sets="attsPageSequence">
<xsl:apply-templates />
</fo:page-sequence>
</fo:root>
</xsl:template>
<xsl:template match="md:document">
<xsl:call-template name="header">
<xsl:call-template name="footer" />
<fo:flow flow-name="xsl-region-body">
<xsl:apply-templates />
</fo:flow>
</xsl:template>
基本版面についてはMarkdown側で干渉するところは特にないので名前付きテンプレートとして省略しました*3。coverPageテンプレートで本文とは別に表紙を作っています。
著者情報を「レベル1見出しの直後の段落を著者名にする」といった形でMarkdown側で記述するようにもできるでしょうが、
やはり「Markdownでは記述しない箇所を決める」という割り切りがある程度の単純さを保つために必要に思えます。
ブロックの例を見てみましょう。
<xsl:attribute-set name="attsCode">
<xsl:attribute name="font-family">Source Code Pro, 源ノ角ゴシック Code JP, monospace</xsl:attribute>
<xsl:attribute name="font-size">12q</xsl:attribute>
<xsl:attribute name="xml:space">preserve</xsl:attribute>
</xsl:attribute-set>
<xsl:attribute-set name="attsCodeBlock" use-attribute-sets="attsCode">
<xsl:attribute name="linefeed-treatment">preserve</xsl:attribute>
<xsl:attribute name="axf:border-bottom-left-radius">3pt</xsl:attribute>
<xsl:attribute name="axf:border-top-right-radius">3pt</xsl:attribute>
<xsl:attribute name="border">solid 1.5pt gray</xsl:attribute>
<xsl:attribute name="background-color">silver</xsl:attribute>
<xsl:attribute name="padding">3mm</xsl:attribute>
<xsl:attribute name="space-before">4mm</xsl:attribute>
<xsl:attribute name="space-after">4mm</xsl:attribute>
</xsl:attribute-set>
<xsl:template match="md:code_block">
<fo:block xsl:use-attribute-sets="attsCodeBlock">
<xsl:apply-templates />
</fo:block>
</xsl:template>
コードブロックです。CommonMarkのASTにはinfoとして最初の行の```xml、「xml」が格納されていますが、
1からシンタックスハイライトを行うのはつらいので、背景色やボーダー、等幅フォントを指定しています。< xsl:templatye match="md:code_block">の箇所はかなりシンプルにできていますね。
<xsl:attribute-set name="attsListBlock">
<xsl:attribute name="space-before">1rem</xsl:attribute>
<xsl:attribute name="space-after">1.4rem</xsl:attribute>
<xsl:attribute name="provisional-label-separation">2mm</xsl:attribute>
<xsl:attribute name="provisional-distance-between-starts">5mm</xsl:attribute>
</xsl:attribute-set>
<xsl:template match="md:item">
<xsl:variable name="type" select="../@type" />
<fo:list-item xsl:use-attribute-sets="attsItem">
<fo:list-item-label end-indent="label-end()">
<fo:block text-align="end">
<xsl:attribute name="axf:number-transform">
<xsl:choose>
<xsl:when test="$type = 'bullet'">
<xsl:text>circle</xsl:text>
</xsl:when>
<xsl:when test="$type = 'ordered'">
<xsl:text>1.</xsl:text>
</xsl:when>
</xsl:choose>
</xsl:attribute>
<xsl:number />
</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block>
<xsl:apply-templates />
</fo:block>
</fo:list-item-body>
</fo:list-item>
</xsl:template>
<xsl:template match="md:list">
<fo:block-container column-count="2">
<fo:list-block xsl:use-attribute-sets="attsListBlock">
<xsl:apply-templates />
</fo:list-block>
</fo:block-container>
</xsl:template>
箇条書き(リスト)です。それぞれの項目が短い場合、1段だと余白がかなり空いてしまいます。AH Formatterではブロックコンテナでも段組みを別に指定できるので、2段組にしてみました。今回使用してはいないものの、数字付き箇条書きでも動作します。CommonMarkのASTでは箇条書きは同じlist構造でtypeプロパティの値が違う形なので、HTMLのようにulとolが分けられているより対応が楽だと感じました。入れ子のリストでの記号の変更も、 <xsl:variable name="nest" select="count(ancestor::list)" />のようにすれば階層が割と簡単に分かりそうです。
ところでこの組版結果は想定とズレていて、ASTの<item>の内部で一度<paragraph>が使われているため、ラベルと項目の間に、通常の段落を想定した段落開始のインデントが入ってしまっています。
インライン記法の変換例を見てみましょう。
<xsl:attribute-set name="attsEm">
<xsl:attribute name="axf:text-emphasis-style">dot</xsl:attribute>
<xsl:attribute name="axf:text-emphasis-font-family">Kenten Generic</xsl:attribute>
<xsl:attribute name="axf:text-emphasis-skip">spaces punctuation symbols narrow</xsl:attribute>
</xsl:attribute-set>
<xsl:template match="md:emph">
<fo:inline xsl:use-attribute-sets="attsEm">
<xsl:apply-templates />
</fo:inline>
</xsl:template>
和文の簡易マークアップで悩ましいemとstrong。AH Fromatterでは圏点の拡張がありますから、emでは圏点を使用してみました。
今回の記事は『スタイルシート開発の基礎 XMLとFOで簡単な本を作ってみよう』[5]を片手に、適宜読み替えながら書き進めてみました。Markdownからの変換はLaTeXやHTML経由でのものが多く、それぞれに長所や特徴がありますが、XSL-FOで1つ1つ記法とFOを確かめながらというのも、組版全般やXSLTの学習がしっかり進んだように感じ、良いものです。
体感としては、一般的なプログラミング言語で同程度の内容を書くよりも3倍くらいの行数になっている感じがします。
*1XSLTとXSL-FOの記法について学習中の人間が執筆しました。誤りが含まれる箇所があるかもしれません。
*2 「不足する」他のものとしては、表示言語などもそうです。多くのXMLアプリケーションでは「xml:lang」や「id」といった情報は<xsl:copy-of>を利用して入力から出力へそのまま渡せますが、CommonMarkのASTにその情報は含まれません。<html_block>の情報としてマークアップを処理する、言語ごとにファイルを分けるなど、手軽な処理とはいかないでしょう。
*3 <template match="/">は個人的な感覚としては一般的なプログラミング言語におけるmain関数に近いので、切り出せる処理を適時追い出し、子の箇所で必要な値(で子からの取得が面倒なもの)については<xsl:with-param>で渡しています。<xsl:attribute>は調整する可能性がないものは<xsl:template>内でハードコード、そうでなければxsl:use-attribute-setsで別に切り出しています。2ページもない文書を処理するにしては冗長かもしれません。
参考資料
- [1] https://github.com/commonmark/cmark
- [2] https://commonmark.org/
- [3] 構造化文書とは – アンテナハウス
- [4] Markdown + XSL → PDF
- [5] スタイルシート開発の基礎 – アンテナハウス
















