カテゴリー別アーカイブ: 構造化文書

DITA-OTでソースコードを書くならcoderefが便利

DITAでソースコードを書くときはcodeblock要素を使います。

(HTML5では引用ブロックやcodeblock(pre/code)もfigureの子孫として記述する方法がよく見られます。個人的な感覚として、英語だとキャプションに「figure 1」のようにしてソースコードが記載されていても違和感はないのですが、日本語で「図1」となっているところにソースコードが記載されていると違和感があります。)

さて、codeblockの中をどう書くかについて、方針はおおむね次の2つです。

  • 直接書く。XMLの<や>は
    • &lt;のようにして書く
    • xml-mentionドメインのタグを使って書く
  • coderefを使う

今回は記事タイトルにもあるように、coderefを使う方法が便利という話です。

DITA-OTではcodeblockでの処理について、仕様から拡張しています。記事タイトルが「DITAでcodeblockを書くときは~」ではないのはDITAの仕様ではないからです。なお、記事を作成する際に試行した環境はDITA-OT 3.7.1となります。

Extended codeblock processing DITA-OT

拡張内容は幾つかあるのですが、先に述べた通り、今回紹介するのはcoderefについてです。

codeblockにはテキストをそのまま記述することもできますが、XMLタグ、というか<などがきっちり処理されてしまうため、XMLやHTMLをソースコードとして例示するのは結構大変です。そこでcoderefです。

coderefは外部ソースコードを(主にテキストとして)参照し、結果を展開してほしい場合に使うタグです。@hrefで参照する先を指定します。XMLを参照する場合、@format=”xml”を付けましょう。

coderefの第一の利点は<をエスケープしなくて済む点です。これについてはDITA仕様のうちです。

coderefを使ったコードブロック
<codeblock xml:space="preserve"><coderef href="hoge.xml" /></codeblock>

ただ、実際のXMLファイルというのは結構行数が嵩みます。「coderefで参照する用にコード片を別ファイルに保存して……」というのはメインテナンス性からするとあまり歓迎できません。そこでDITA-OTの機能によって行数を制限します。

coderefの拡張記法は#から続くフラグメントによるものです。これに対応していない、DITA-OT以外のDITA処理系で使われても、ファイルの全行が出力されるだけで済みます。……結構大変なので、keyrefで切り出して切り換え可能にしておくのが良いかもしれませんね。

行数の記法はドキュメントにある通り、#line-range(<start>,<stop>)またはRFC 5147の記法で#line=<start><end>のようにして開始行、終了行を指定します。

このままで十分便利ですが、「元のソースコードを弄ったらトピックファイルで指定している行位置も変更しなくてはいけないのだろうか」と疑問を持たれたことでしょう。それはあまりメインテナンス性が良くありませんね。ということで、任意文字列を行位置の識別子にする方法が提供されています。
#token=<start-text>,<end-text>を指定すると、ソースコード中のstart-textがある行の次行からend-textがある行の前行までが範囲として取り出されます。想定としてはコメントアウトした行にstart-text、end-textを書いておく形のようなので、あまりトリッキーなことはしない方が良いでしょう。ほかにも幾つかの機能がDITA-OTのページで紹介されていますが、プラグインや処理系依存の機能もあるようなので都度確かめて使うと良いでしょう。

coderefのstart,end用文字列を追加したXML
<!-- example1start -->
<fo:block><fo:inline>Title</fo:inline></fo:block>
!-- example1end -->

ほか、coderefというcodeblockの中で更に別のタグを使うことのメリットは、@hrefで参照した箇所と、直接書く箇所をcodeblockの中で行える点です。

coderefを使ったコードブロック

<codeblock xml:space="preserve"><coderef href="hoge.xml#line-range(1,5)" />
... <!-- 直接書いた部分 -->
<coderef href="hoge.xml#line-range(10,15)" /></codeblock>

1-5行目、「…」を書いて10-15行目、なんて表示も可能になります。

そんなcoderef、DITA 2.0で若干の変更が入ることが現在のドラフトで言及されています。といってもエンドユーザがトピックを記述する上ではそう変化はなく、主に仕様上の立ち位置がより整理されるということのようです。


来週に迫る「DITAで本を書いてAH XSL Formatterで自動組版する」ウェビナーと溢れ話

以前ブログ記事でも告知しました*1「DITAで本を書いてAH XSL Formatterで自動組版する」ウェビナーを来週8/10(火)に開催します。

以前の記事で触れましたように、このウェビナーは『AH XSL Formatter拡張仕様使いこなしガイド』の制作報告であるものの、プレゼンの性質と時間の都合上、省略することはどうしても出てきてしまいます。

本記事では、省略した中から「実際の制作はLightweight DITAから始めた」ことについて書きたいと思います。プレゼンから省略された一番の理由は「説明がややこしくなる」なので、一度内容を忘れてウェビナー終了後にまたご覧になっていただくのも良いかもしれません。

実際の制作はLightweight DITAから始めた

Lightweight DITA

Lightweight DITA(LwDITA)はDITAのサブセットであるXDITAと、XDITAと互換があるよう設計したHTML5で記述するHDITA、MarkdownとYAML Frontmatterで記述するMDITAを指します。DITA-OTの新しめのバージョンではformat="mdita"のようにして通常のDITAトピックと同様に処理可能です。
LwDITAについては以前に少し記事を書きました。併せてご覧ください。

原稿形式選定

原稿形式選定にあたってのライバルは、様々な弊社出版物の実績があるCAS-UB、『Office Open XML Format入門』で利用されたsimpleDocといったものがありました。それらからの選定にあたって「執筆協力予定メンバー全員がそれらの原稿形式に慣れているわけではない」ということがあり、「原稿をプレーンテキストまたはHTMLで受けとれるならオーサリング作業は何とかなるだろう」という考えがあり、とりあえずということでLightweight DITAを採用することにしました。

  • マークアップが不足して困ったとき別形式に移行しやすそう
  • MarkdownやHTMLなら何とか書けるだろう

「~だろう」というふわっとした状態で制作をスタートしてしまったことが大きな反省点です。内部調整的な話は「連絡・相談をしっかりしよう」ということに尽きるのですが、技術的な面でも問題がありました。技術的な面と書きましたが、初歩の話です。

反省点

  • DLとTableの使い分けははっきりさせておこう
  • LwDITAのマークアップは最低限しかないので、DITAへの移行時にどうするかを詰めておく
  • LwDITAでもkeyrefは使える

DLとTable、これは仕様の表についての話ですね。仕様についての書籍ですので、山のように登場します。
HTMLのセマンティクスでも、仕様や会社情報の列挙にtableを使うべきかDLを使うべきかというのは混乱しやすいですし、一概に片方のみを正解とも言えません。今回の仕様の表については「1箇所につき1仕様」「左にラベル、右に内容という構成」を考えると、DLを使用していればミスを減らせたのではないか、と後から思いました。また、MDITAの簡易マークアップによる表はsimpletableになりますが、(望ましい形への自動変換を自前で用意しない限り、)ページ数その他についてシビアになる制作物では使わない方が良いでしょう。なんならテーブルマークアップは手で書かずにデータ変換処理によって用意した方が良いです。

MDITAの簡易マークアップによる表

| Header |
|--------|
| Cell   |

LwDITAのマークアップは最低限であることについて。XMLタグの&lt;や&gt;を表示時に補ってくれるXMLドメインなどはLwDITAでは使えません。MDITAではattributeに変換時のためのclass(HTML)なども使えませんから、使い分けが想定されるのであればトピック数が数百ファイルになる前に何らかの対処が必要でした。

LwDITAでもkeyrefは使えます。主に図版のパスの問題ですね。実は執筆当初使い方が分からかったので後回しに(書き方が間違っており上手く処理されなかった)したところ、後半の作業で牙を剥きました。

LwDITAを諦めた最たる理由はindextermが使えなかったことですが、これについては以前の記事で触れていますので割愛します。

こういった反省点を基にLwDITAをもっと上手くライティング形式として活用できる展望はあるのですが、最近はXMLでの読み書きに抵抗が薄くなったため、その機会はそうそう無いかもしれません。

ということで、来週8月10日(火)16時から、ちょっと一息アンテナハウスウェビナー『DITAで本を書いてAH XSL Formatterで自動組版する』を開催しますので、ご参加いただければ幸いです。また、『AH XSL Formatter拡張仕様使いこなしガイド』*2もよろしくお願いします。

DITAで本を書いてAH XSL Formatterで自動組版する

日時
2021年8月10日(火)16:00~17:00
概要
2021年5月18日に公開/販売した『AH XSL Formatter 拡張仕様使いこなしガイド』の制作報告を通し、XML執筆からPDFを作る過程の知見をご紹介します。
DITAについてや、DITAでの書籍制作における実例の紹介や、DITAを扱うときの注意事項など、自動組版やXMLの使い方、DITAに興味がある方、Formatterユーザーさん、必見です!
内容紹介・お申込みページ
こくちーずプロからお申し込み:https://www.kokuchpro.com/event/20210810/
Zoomウェビナーへ直接お申込みいただく場合: ウェビナー登録ページ

アンテナハウスPDFチャンネルで、ちょっと一息アンテナハウスウェビナーの録画をご覧いただけます。

弊社では、昨年8月より、原則として第2、第4火曜日の16時からにZoomのウェビナーによる、ちょっと一息アンテナハウスウェビナーを開催しております。

ちょっと一息アンテナハウスウェビナーのご案内ページ

これまでに開催したちょっと一息アンテナハウスウェビナー・シリーズは、延べ10回になります。

2020年分のアンテナハウスウェビナー・シリーズについては、こちらでご確認いただけます。

アンテナハウス ウェビナー 動画一覧 2020

過去のウェビナーは録画を、YouTubeの「アンテナハウスPDFチャンネル」に公開しております。YouTubeでは、できるだけ短時間でご視聴いただけるように、各回の録画を数個に分割しておりますので、お気軽にどうぞ。

アンテナハウスPDFチャンネル

2021年1月12日に本年最初の一息アンテナハウスウェビナーとして「申請・登録手続きのデジタル化を進めるならこれだっ! 書き込みソフトなしでも内容を記入できる、魔法のようなPDFファイルとは? その作成方法と活用方法のご案内」を開催しました。当日の録画につきましては、アンテナハウスPDFチャンネルで明日(1月26日)から3回にわたって公開の予定です。

また、本日1月26日16時より、本年2回目の一息アンテナハウスウェビナーを開催いたします。

テーマは、「PDF自動生成超入門 PDF自動生成へ挑戦する人のための始めの一歩」です。詳細はこちら(https://www.kokuchpro.com/event/20210126/)で確認いただけます。当日でも構いませんので、お時間の余裕ができましたら、ご参加ください。こちらで、Zoomへ直接お申込みいただくこともできます。【終了しました】


アンテナハウスPDFチャンネルで「PDF自動生成超入門 PDF自動生成へ挑戦する人のための始めの一歩」のウェビナーの録画を編集した動画を公開しました。
https://www.youtube.com/watch?v=8td0JNKaVes

PDF自動生成超入門 PDF自動生成へ挑戦する人のための始めの一歩

【動画公開しました】1月26日火曜日のちょっと一息・アンテナハウスウェビナーは「PDF自動生成超入門 PDF自動生成へ挑戦する人のための始めの一歩」

アンテナハウスでは、昨年8月から第2、第4火曜日の16時から「ちょっと一息・アンテナハウスウェビナー」*と題する無償のZoomウェビナーを開催しています。

ちょっと時間の余裕があれば、お気軽にご参加いただけて有用な知識を持ち帰っていただける、肩が凝らないウェビナーを目指しています。

来週は、「PDF自動生成超入門 PDF自動生成へ挑戦する人のための始めの一歩」と題して、自動組版は初めてという人向けに、弊社が目指す自動組版のお話をさせていただきます。

自動組版にはDTPソフトを組版エンジンとして使う方式から、自動組版専用エンジンを使う方式までいろいろな方法がありますが、26日のウェビナーでは、自動組版専用エンジンであるAH Formatter**を使う方法についての簡単な説明を致します。


※画像をクリックすると「こくちーず」の本ウェビナーご案内ページに移動します。
終了しました。

本ウェビナーご視聴いただければ、今すぐでなくてもいずれは必ずお役に立つ情報を得られるでしょう。自動組版に少しでも関心をお持ちの方はぜひご参加ください。


2021年2月:追記ウェビナーの録画を編集した動画を公開しました。

なお、「ちょっと一息・アンテナハウスウェビナー」の過去開催分は録画を、YouTubeのアンテナハウスPDFチャンネルにて公開しています。
チャンネル登録をお願い致します。

Antenna House YouTube Channel

アンテナハウスPDFチャンネル


Antenna House XML Services
AH Formatter

XProcの紹介

makeが良い、悪い」という話が一部で盛り上がっているのを見ました。
結局ツールなので、「用途に向いていなくても慣れなどを取る」か「学習が必要でも別のものを使う価値が見合う」かといった、状況に合わせた選択が必要になるでしょう。個人的にはファイルの絶対パスをハードコードするのは可能な限り避けてほしいですが。

ビルドツールにも様々な得手不得手があるものですが、XMLに特化したものもあります。そんな導入で、XProcの話題です。

XProc: An XML Pipeline LanguageはXML文書やその他の処理を記述するための言語です*1
W3CのページのAbstractには“Pipelines generally accept zero or more XML documents as input and produce zero or more XML documents as output.”とあります。ステップ処理として記述ができる、ファイル操作についてもある程度はできるなど他にもありますが、XSLTとは目的も書き味も結構違います。

2010年に1.0がW3C仕様として勧告された後、勧告としてはそれが最新の状態がまだ続いています。しかし、2020年8月19日にXProc 3.0の最終ドラフトが上がっており*2、期待が持てるようになってきました。Editor’s Draftは8月31日に更に更新されていますが。ちなみに2.0は諸般の事情で見送られています。

また、XMLPressから2020年4月に『XProc 3.0 Programmer Reference』(Erik Siegel)*3が刊行されています。W3CのXProc仕様のEditorであるNorman Tovey-Walsh氏による推薦文もついていますし、Erik Siegel氏も XProc 3.0 editorial teamのメンバーです。
XProc 3.0についてはドラフト版仕様とこの本を読むのが確実でしょう。

XProc 3.0でのもっとも大きな改良点はXSLT同様にXPath 3.1に対応したことだと思いますが、今回はXProcの雰囲気だけ紹介します。


<!-- 1. Pipeline document: -->
<p:declare-step xmlns:p="http://www.w3.org/ns/xproc"
  xmlns:xs="http://www.w3.org/2001/XMLSchema" version="3.0">

  <!-- 2. Pipeline port declarations: -->
  <p:input port="source" primary="true" />
  <p:output port="result" primary="true" />
  <!-- 3. Convert document to HTML: -->
  <p:xslt>
    <p:with-input port="stylesheet" href="xsl/basic-conversion.xsl" />
  </p:xslt>
</p:declare-step>

上のコードは『XProc 3.0 Programmers Reference』*3「Getting started with XProc」にある例です。
<p:declare-step>がルート要素です。inputやoutputを省略できる<p:pipeline>も使用できますが、
2行程度では大して労力は変わらないですね。

XMLSchemaのネームスペースが宣言されているのは、XSLT同様asで型を明示できるからです。

書かれた処理を簡単に書けば、「入力をxsl/basic-conversion.xslのXSLTで処理し、出力へ渡す」ということになります。しかし、よくわからないプロパティportの存在がありますね。それぞれの指定で想像が付くかと思いますが、inputによる入力を処理対象(source)というportにつなぎ、with-inputによる入力は(XSLT)スタイルシートというportにつなぎ、このスタイルシートによってsourceを処理します。そしてresultというportにつながった出力へ結果を渡す、という流れです。こう書くと同語反復をしているように感じられるかと思いますが、それぞれのportにきたものをステップ処理していく流れが直観に近い記述で書けるということです。他のportとしては、スキーマの検証のスキーマを渡すためのschemaなどがあります。
<p:xslt><p:validate-with-xml-schema>はデフォルトのstepです。portは決まったstepと結びついていて、portに渡したものは結びついたstepが記述されていれば、そのstepで処理されます。デフォルトのstepとport、そして独自のstepと結びついた独自のportを記述していくことがXProcの基本となります。

*1 https://www.w3.org/TR/xproc/
*2 https://spec.xproc.org/lastcall-2020-08/head/xproc/
*3 https://xmlpress.net/publications/xproc-3-0/



XSLT 3.0(XPath 3.1)JSONをXMLに変換せずに利用する

先週はXMLの形にしたJSONを、XMLにしないまま扱う方法について紹介します。CSLはでてきません。

XPath 3.1*1で名前にJSONが入る関数は4つです。

  • fn:parse-json($json-text as xs:string?, $options as map(*))
  • fn:json-doc($href as xs:string?, $options as map(*))
  • fn:json-to-xml($json-text as xs:string?, $options as map(*))
  • fn:xml-to-json($input as node()?, $options as map(*))

$optionsについては説明しません。JSONをテキストとして引数にとるのがfn:parse-json()fn:json-to-xml()
fn:json-doc()は外部JSONファイルを読み込むときなどに指定します。fn:unparsed-text()で取得したテキストをfn:parse-json()へ適用するのと
大体同じことを行います。今回取り上げるのはfn:parse-json()fn:json-doc()についてです。

mapとarray

関係する名前空間は次に挙げるものです。

  • fn=”http://www.w3.org/2005/xpath-functions”
  • map=”http://www.w3.org/2005/xpath-functions/map”
  • array=”http://www.w3.org/2005/xpath-functions/array”

XPath 3.1のmap構造とarray構造は一般的なプログラミング言語におけるそれとほぼ同じもので、JSONオブジェクトをそのままの形で格納できます。
先週登場したJSONを見てみましょう。

{
    "items": [
        {
            "id": "7646893/2E3MJB9A",
            "type": "book",
            "title": "スタイルシート開発の基礎",
            "publisher": "アンテナハウス株式会社",
            "publisher-place": "Tokyo",
            "event-place": "Tokyo",
            "ISBN": "978-4-900552-23-4",
            "language": "ja",
            "author": [
                {
                    "family": "アンテナハウス株式会社",
                    "given": ""
                }
            ],
            "issued": {
                "date-parts": [
                    [
                        2016,
                        5
                    ]
                ]
            }
        }
    ]
}

このJSONを外部ファイルとして取り込むには次のように記述します。

<xsl:param name="input" >'exported-data.json'</xsl:param>
...
<xsl:variable as="map(*)" name="jsonMap" select="fn:json-doc($input)"/>
<!-- または -->
<xsl:variable as="xs:string" name="json-text" select="fn:unparsed-text($input)"/>
<xsl:variable as="map(*)" name="jsonMap" select="parse-json($json-text)"/>

XMLに変換した先週と、mapやarrayを活用する今回はこの後がまったく異なります。

次の記述で、先頭のtitleを一息で取得します。

<xsl:value-of select="map:get(array:head(map:get($jsonMap, 'items')), 'title')" />

まず、最も外側のmapから、itemsのキーを持つ要素を取得します(map:get($jsonMap, 'items'))。
キーitemsの値はarray型です。このままmap:get()を行おうとしてもできませんから、arrayの先頭を取り出すarray:head()を使用しています。ところで上の記述はパイプ演算子を使えば次のように書けます。

<xsl:value-of select="map:get($jsonMap, 'items') => array:head() => map:get('title')" />

mapのarrayでは、map:find()を利用することで指定したキーの値を配列で得ることもできます。挙動の詳細はXPath 3.1*1かXSLT 3.0*2のページを確認してください。

typeの値が何種類かに決まっていて、その種類を元にif文の判定をしたいのであれば、次のように書けます。

     <xsl:variable as="map(*)" name="item" select="map:get($jsonMap, 'items') => array:head()" />
      <xsl:if test="map:get($item,  'type') =  'book' or 'proceedings'">
      <xsl:value-of select="map:get($item, 'type')" /> <!-- book -->
      </xsl:if>

もちろんXMLの構造に変換しても同じことはできますが、より一般的なプログラミング言語に近い形で扱えています。

mapやarrayとして扱うために、JSONテキストとして記述してパースしたり、select="map{"key":value}"のような書き方をする以外に、<xsl:map><map-entry>を使うことができます。

mapの操作はエントリの追加やキー指定での削除などの他、map:merge()によるmapの統合、map:for-each()によるmap要素単位での関数適用などが可能です。arrayの方はarray:fold-left()array:fold-right()の畳み込み関数や、array:for-each()でarrayの要素ごとに関数適用などなど、XML形式を扱うよりもすっきりした構文で記述が可能な関数が用意されています。

注意しなければならないこととして、mapであるかarrayであるかを間違えるとうまく値を取り出せません。$itemnodeでもないので、直接xml-to-json()は使えません。

仕様やSaxonのドキュメントとにらめっこをしながら紹介してきたXSLT 3.0とXPath 3.1のJSONの扱いですが、誤りなどありましたらご指摘ください。

*1 https://www.w3.org/TR/xpath-31/
*2 https://www.w3.org/TR/xslt-30/


参考資料



Citation Style Languageの話(2) – 引用データJSONのXML化

前回引用データとスタイルの分離の必要性については述べていたので、CSL自体の歴史を見てみました。

Citation Style Languageの歴史について、
CSLのページ*1によれば、Bruce D’Arcus氏を中心に開発され、初期はZoteroのSimon Kornblith氏
がコントリビュートしていました。近年は Frank G. Bennett, Jr. と Rintze M. Zelleによって開発が主導されています。リンクが張られている2010年9月の外部記事はすでに読めなくなっていて、Blogは1.0のニュースリリース*2からです。

Wikipediaの記事*3にはその前身はBiblioXという取り組みであると記載されているのですが、2020年8月31日現在BiblioXのページは消失しており、辿ることが少し難しいようです。BiblioXの発表は2004年ですので、合わせれば15年以上の歩みとなります。

脱線しますが、URLが失効しあるものの歴史を辿るのが難しいというのは堪えます。Web Archiveなどのプロジェクトにしても、無限、無制限というわけにはいきません。それでも、参照した情報元が分かるよう引用情報を記述することの大事さを噛みしめています。

引用データのJSONをXSLTでXML化する

さて、CSLの引用データを確認してみましょう。Zoteroの出力一覧に「Citation Style Language …」という文字列が見えます。ポチッとな……無事ダウンロードされました。

「export-data.json」が。


{
    "items": [
        {
            "id": "7646893/2E3MJB9A",
            "type": "book",
            "title": "スタイルシート開発の基礎",
            "publisher": "アンテナハウス株式会社",
            "publisher-place": "Tokyo",
            "event-place": "Tokyo",
            "ISBN": "978-4-900552-23-4",
            "language": "ja",
            "author": [
                {
                    "family": "アンテナハウス株式会社",
                    "given": ""
                }
            ],
            "issued": {
                "date-parts": [
                    [
                        2016,
                        5
                    ]
                ]
            }
        }
    ]
}

引用データそのものは文書レイアウトのような複雑な構造を持ちませんし、XML形式である必要はないですからね。とはいえXSLTでこのデータを扱うにはどこかの段階でXMLに変換する必要があります。幸いなことに2020年のXSLTはJSONだって簡単に扱えます。今回はJSONがXSLT3.0で扱えることを示します。

XSLT 3.0 (というよりXPath 3.1)ではmap、arrayという頼もしい機能が用意されていますが、今回は構造に登場するだけです。次のテンプレートでは、外部ファイルであるexport-data.jsonを読み込んでXMLとして表示します。unparsed-text()でテキストファイルとしてJSONを読み込み、読み込んだJSONをjson-to-xml()でXMLにします。


<xsl:param name="input" >export-data.json</xsl:param>     
<xsl:template name="xsl:initial-template">
      <xsl:copy-of select="json-to-xml(unparsed-text($input))"/>
</xsl:template>

結果のXMLを次に示します。

<?xml version="1.0" encoding="UTF-8"?><!--export-data.xml-->
<map xmlns="http://www.w3.org/2005/xpath-functions">
  <array key="items">
    <map>
      <string key="id">7646893/2E3MJB9A</string>
   <string key="type">book</string>
      <string key="title">スタイルシート開発の基礎</string>
      <string key="publisher">アンテナハウス株式会社</string>
      <string key="publisher-place">Tokyo</string>
      <string key="event-place">Tokyo</string>
      <string key="ISBN">978-4-900552-23-4</string>
      <string key="language">ja</string>
      <array key="author">
        <map>
          <string key="family">アンテナハウス株式会社</string>
          <string key="given"/>
        </map>
      </array>
      <map key="issued">
        <array key="date-parts">
          <array>
            <number>2016</number>
            <number>5</number>
          </array>
        </array>
      </map>
    </map>
  </array>
</map>

XML形式で出力せずに扱う方が便利なこともありますが、とりあえず今回のところは得られた引用データのXMLから一部を抜き取ってFOにしてみます。

<xsl:template match="j:string[@key='title']">
    <fo:inline font-family="sans-serif">『<xsl:value-of select="." />』</fo:inline>
</xsl:template>
<xsl:template match="j:map[@key='issued']">
    <xsl:apply-templates select="j:array[@key='date-parts']"/>
</xsl:template>
<xsl:template match="j:array[@key='date-parts']">
<fo:inline><xsl:value-of select="./j:array/j:number[1]" />年</fo:inline>
</xsl:template>
<xsl:template match="j:string[@key !='title']">
</xsl:template>

これをexport-data.xmlに適用します。かなり省略していますが、
export-data.xmlのtitledate-partsから年の値を取り出し、加工して出力できました。

<fo:inline font-family="sans-serif">『スタイルシート開発の基礎』</fo:inline><fo:inline>2016年</fo:inline>

CSLからXSLT、XSL-FOへの完全な変換まで到達予定でしたが、XPath3.1やXSLT3.0の機能を調べだしたら今回の記事に間に合わなくなったため、少し時間を空けて再挑戦したいところです。

*1 https://citationstyles.org/about/
*2 https://citationstyles.org/2010/03/22/citation-style-language-1-0/
*3 https://ja.wikipedia.org/wiki/Citation_Style_Language

関連記事

  1. Citation Style Languageの話(1)

Citation Style Languageの話(1)

個人用途での電子書籍管理は引用管理ソフトウェアであるZotero[1]を利用しています。物理的な文書も登録管理できますが、蔵書量の関係からまだ手をつけていません。休日にちまちまと登録しているのですが、休日の度に本が増えるので終わりはしばらく来なさそうです。
書きもので技術的内容・事実に依拠した内容を扱うにあたっては参考文献が重要になりますが、参考文献の書式は学会や論文誌によって異なっているものです。つまり、書誌情報と書式を分離して管理、利用する仕組みが求められることになります。このうちオープンで普及しているものとしてはBibTeXといったものがあります。どんなものを使おうとおこる問題ではありますが、BibTeXを使うにはBiBTeXの記法やBiBTeXを処理するツールへの習熟が必要です。

さて、Zoteroではさまざまな形式で管理している参考文献リストを、プラグインによってMicrosoft Wordなどにも出力できます。そしてより柔軟にこのリストを扱うフォーマットとしてCitation Style Language(CSL)[2]というXMLに基づくオープンフォーマットが利用できます。
CSLを処理するスタンドアロンのソフトウェア*もありますが、XMLですのでXSLTによる処理も可能です。次のCSLはCSL primer[3]のページから一部抜粋しています。

<?xml version="1.0" encoding="utf-8"?>
<style xmlns="http://purl.org/net/xbiblio/csl" version="1.0" default-locale="en-US">
<!-- Generated with https://github.com/citation-style-language/utilities/tree/master/generate_dependent_styles/data/asm -->
<info>
<title>Applied and Environmental Microbiology</title>
<id>http://www.zotero.org/styles/applied-and-environmental-microbiology</id>
<link href="http://www.zotero.org/styles/applied-and-environmental-microbiology" rel="self"/>
<link href="http://www.zotero.org/styles/american-society-for-microbiology" rel="independent-parent"/>
<link href="http://aem.asm.org/" rel="documentation"/>
<category citation-format="numeric"/>
<category field="biology"/>
<issn>0099-2240</issn>
<eissn>1098-5336</eissn>
<updated>2014-04-30T03:45:36+00:00</updated>
<rights license="http://creativecommons.org/licenses/by-sa/3.0/">This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License</rights>
</info>
</style>

この基本情報と、テキスト中での引用書式、参考文献リストでの引用書式、
ロケールによる表示変更、その他書式のためのマクロといった要素を組み合わせたものがCSLの構造です。
つまり、XSLTを記述する方針は、テキスト中での引用書式cs:citation
参考文献リストでの書式cs:bibliographyの記述を元に出力書式を用意し、そこに情報を流しこんで出力を行うことになります。
cs:macroの内の記述方法などはXSLTと似通ってはいますが、if文でelse節が使えたり、variableやtextの使い方が異なるなど注意を要します。

来週はCSLの経緯の概略と、CSLからXSL-FOへの変換に挑戦予定です。


* CSLを利用できるソフトウェアのリストはCSLのトップページ[2]にあります。

[1] https://www.zotero.org/
[2] https://citationstyles.org/
[3] https://docs.citationstyles.org/en/1.0.1/primer.html

関連記事

Citation Style Languageの話(2) – 引用データJSONのXML化


海外ウェビナー動画「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の字幕機能を使えば、日本語字幕付きでご視聴いただくこともできます。
字幕の設定方法は、

  1.  動画右下の設定アイコン(歯車マーク)をクリック
  2. 「字幕」をクリック
  3. 「日本語」にチェックを入れる

以上の設定で日本語字幕付きでご視聴いただけます。



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



アンテナハウス ウェビナー スケジュール一覧 2020


CommonMark文書をXSL-FO経由でPDFへ変換する

前回、cmark[1]でCommonMark[2]のASTをXML形式で得られることを確認しました。

記事中で登場するコードの動作についての保証はできかねます*1。ご了承ください。

今回の記事で使用した環境は次になります。

文書はその目的に適した構造を持ち、組版では多くの場合その構造に沿ってマークアップを行うでしょう[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. [1] https://github.com/commonmark/cmark
  2. [2] https://commonmark.org/
  3. [3] 構造化文書とは – アンテナハウス
  4. [4] Markdown + XSL → PDF
  5. [5] スタイルシート開発の基礎 – アンテナハウス


Antenna House Formatter

DITA/XML Service Antenna House


関連記事


Pages: 1 2 3 4 5 6 7 8 9 10 ... 15 16 17 Next