カテゴリー別アーカイブ: XML-DITA

来週に迫る「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タグの<や>を表示時に補ってくれる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ウェビナーへ直接お申込みいただく場合: ウェビナー登録ページ

XSL-FO試行錯誤 変換後を意識しながら独自XMLを考える

業務ではなくプライベートの話となるのですが、自分用のXML規格を考えたり、そのXMLを変換するXSLTを書いたりということをしています。

自分用のXMLはさらに自分用の軽量マークアップからの変換によって得ることを想定しているので、スキーマまで作りこもうと日々ジリジリと進めています。

XSLTは組版用でないXMLを組版用のXSL-FOに変換できるわけですが、「軽量マークアップとして書きやすく、XSL-FOやHTMLにもしやすい」XMLというのはなかなか難しいなと感じています。

脚注はDITAではfnタグでマークアップします。XSL-FOではfootnote、footnote-bodyに変換されるでしょう。CSS Paged mediaではspanにした後フロートさせることが多いようです。
Webページ、HTML5的には脚注文の位置はasideの中あたりが良いのでしょうか?ただ、HTMLページの一番下(フッタ)に置いてしまうとPDFや紙でのソレよりも遥か下に行ってしまい、後注、しかもappendix的な配置になってしまうかもしれません。
では脚注用のfootnoteタグを用意するのではなくnoteタグを用意してレイアウトは属性にしましょうか?

変換先がXSL-FOであれば出力は1ファイルが望ましいでしょう。
しかしたとえばDITAなら?元の独自XMLはマップやトピックをどのように扱っていると好ましいでしょうか?独自XML自体が外部ファイルをどう扱うかも重要です。XIncludeなどを援用する、しない。うーむ。

メインの構造をまたぐ構造、たとえば複数段落に対する注釈などについてはrange-startとrange-endのようなタグを作る方式である程度しのげますが、変換後に納得できる構造にできるかは悩ましいところ。

そんなことを考えていると休日が過ぎています。
XMLアプリケーションはそれぞれがそれぞれの用途のために専門化されているので、中間形式のための独自XML規格というのは実は難しいのかもしれません。

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ウェビナーへ直接お申込みいただく場合: ウェビナー登録ページ


【ちょっと一息アンテナハウスウェビナー】「DITAで本を書いてAH XSL Formatterで自動組版する」を開催します【8/10】

の「ちょっと一息アンテナハウスウェビナー」の告知ページが公開されましたので、当ウェビナー担当者から、ブログ記事でもお知らせします。


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ウェビナーへ直接お申込みいただく場合: ウェビナー登録ページ

元々制作報告については何らかの形で行っていく予定でしたが、この度ウェビナーの形で紹介することになりました。

さて、「ちょっと一息アンテナハウスウェビナー」は平日にお時間をいただくこともあり、時間的にはかなりコンパクトになっています。『AH XSL Formatter 拡張仕様使いこなしガイド』の制作にあたっては、トピックとしては「XMLのこと」「XSL-FOのこと」「XSLTのこと」「PDFのこと」「DITAのこと」「学習にあたって参考にした資料のこと」「入稿のこと」「Antenna House XSL Formatterのこと」「原稿、レビューなどの社内環境のこと」など色々とあり、小さくまとめるのはなかなか難しいのですが、今回のウェビナーでは「既存資産がDITA文書ではない状況で、DITAを原稿とした書籍制作の報告」を軸に行う予定です。

上のように書き出してみるとよく分かりますが、XML組版、そしてそれに関連する技術を使うためには押えておく事項が多く「1から10まですべてやる」というのはかなり体力が必要です。自動組版を検討するにあたって「文書作成から組版までのどの過程を自分たちで行い、どの過程を自分たちではやらないようにするか」を判断する一材料としてもお役に立てるかもしれません。

ウェビナー開催まで少し期間がありますので、ご満足いただけるよう鋭意準備を進めていきます。



『Antenna House XSL Formatter 拡張仕様使いこなしガイド』を公開・発売しました

に『Antenna House XSL Formatter 拡張仕様使いこなしガイド』を公開しました。


Antenna House XSL Formatter 拡張仕様使いこなしガイド | AH Formatter XML関連出版物の紹介

また、弊社Webページで公開しているPDF版のほか、AmazonのPrint On Demand(POD)により印刷版をお買い求めいただけます。

Antenna House XSL Formatter 拡張仕様使いこなしガイド | Amazon

書籍の概要や目次についての詳細は紹介ページに掲載されていますが、この記事では少し短くまとめて紹介します。

本書は、W3C勧告のExtensible Stylesheet Language(XSL)1.1のFormatting Object(FO)から、アンテナハウスが独自に拡張した機能について解説しています。本書は次の4章構成です。

  1. 『ショウケース』
  2. 『Antenna House XSL Formatter 拡張仕様と応用例』
  3. 『PDF出力』
  4. 『その他の拡張』

『ショウケース』はAntenna House XSL Formatterを利用してどのようなレイアウトが可能であるのか、サンプルをどんと出してから、どのようにXSL-FOを記述しているかを項目に分けて説明しています。



『AH XSL Formatter 拡張仕様と応用例』では拡張仕様によって可能になる文書構造、利用例を逆引きで掲載しています。「行いたい表現」から対応する仕様を探す際などにお役に立てていただくことを想定しています。

『PDF出力』『その他の拡張』も拡張仕様の逆引き項目を主内容としますが、PDFに特化した機能は多岐に渡り、セクションでまとめるには大変な量となってしまうため、章として独立しています。『その他の拡張』は印刷やオプション設定など、文書内部とは関連の薄い項目をまとめています。

それぞれのトピックは、ほかのトピックへの言及は極力避け、目次、または最後に配置した仕様一覧へと頻繁にアクセスすることを想定した構成になっています。

さて、本書自体もAntenna House XSL Formatter V7.1改訂2版を用いて制作されています。
LwDITAで下書きを行い、DITAに変換し調整、修正を行った後、DITA-OTとPDF5-MLを基に拡張したプラグインでFOに変換、Antenna House XSL FormatterでAmazon PODの要求する仕様に沿ったPDFを出力、という工程です。この詳細についてはまたブログなどでまとめられればと思います。

Antenna House Formatterでは試用版をご用意しています。XSL-FOによる組版、そして本書で紹介しているような拡張を試用してみたい方はぜひお申込みください。
AH Formatter 評価版のお申し込み



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>で、より自由に行えるようになります。

その他細かいところや全体像などについても、そのうち記事にできればと思います。

参考資料・関連記事


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化


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 11 Next