この記事の「サンプル」で扱ったJATS XML文書サンプルを、HTMLに変換してVivliostyleでページ組版してみます。(長い記事です…)
JATS XMLやDITA1をページ組版というと、XSLT2で変換してXSL-FO3やTeXやCSSで組んで…という解説を見かけますが、ここはJavaScriptでDOMを変換してCSSで組みます。つまりフロントエンドの技術でページ組版しようというわけです。
変換はごく単純で、元のJATS XMLの構造を大胆には変えないようにします。やってみると分かることがあって、この気付きが目的です。というわけで、泥臭く進めていきます。
「JATSが分かる…」の薄い本を作ってみましょう!(薄くないw)
手順
前記の記事で、サンプルをダウンロードして展開したところから始めます。Visual Studio Codeやvscode-xmlは、まだ手を付けてなくて大丈夫、後の個々の画像にサイズを設定してみようで使います。
JATS XML文書をHTMLに変換
次のURLのページでJATS XML文書を読み込んでHTMLに変換して、拡張子だけ.html
に変えたファイルを作り、元のJATS XML文書と同じところに置きます。
この2025_112.xml
を変換して2025_112.html
を作り…
JATSBOOK_XML
├── 2025_112
│ ├── 2025_112.txt
│ ├── 2025_112.xml
│ └── Graphics
│ …
同じフォルダーに置きます:
JATSBOOK_XML
├── 2025_112
│ ├── 2025_112.html # 変換して追加
│ ├── 2025_112.txt
│ ├── 2025_112.xml
│ └── Graphics
│ …
これをすべてのJATS XMLで繰り返します。
(何度もやるようになったら面倒ですよね)
そうなったら一括変換の仕組みを用意するでしょう(この項、修正しました 2025-07-09)。
例えば、次をダウンロードして:
package.json
を用意して:
{
"type": "module",
"dependencies": {
"xmldom": "^0.6.0"
}
}
後のVivliostyleを実行するのとは別のJATSBOOK_XML
フォルダー/ディレクトリを用意して、こんな構造にして:
JATSBOOK_XML
├── 2025_112
├── 2025_122
├── 2025_14
├── …
├── index.js
├── j2hN.js
├── lib
│ ├── convert.js
│ └── sed.js
└── package.json
Macなら、次を実行すると楽……なはずです。ご笑覧ください
find ./2025* -type f -name "*.xml" | while read -r f; do
node index.js "$f" > "${f%.xml}.html"
done
変換後にVivliostyleで組版する用のフォルダー/ディレクトリにコピーします。
ちなみに、この変換処理はみなさんのブラウザーで、みなさんのパソコンなどで実行されます。変換プログラムを置いてあるサーバーでではなく、「変換処理がサーバーに集中して重い」みたいな事態にはなりにくいと思います。
1つのHTMLファイルを複製
HTMLファイルを1つ複製します。奥付用です。
後の構成ファイルに合わせて2025_i.html
を複製することにします…
JATSBOOK_XML
├── …
└── 2025_i
├── 2025_i.html # これを複製
├── 2025_i.txt
├── 2025_i.xml
└── Graphics
こうなります。構成ファイルに合わせて2025_i copy.html
という名前にしてください。
JATSBOOK_XML
├── …
└── 2025_i
├── 2025_i copy.html # 複製
├── 2025_i.html
├── 2025_i.txt
├── 2025_i.xml
└── Graphics
Vivliostyleをインストール
まだインストールしてなければ、次のページの手順でVivliostyleをインストールします:
構成ファイルvivliostyle.config.js
を用意
Vivliostyleの構成ファイルvivliostyle.config.js
を用意します。これはVivliostyleに、どのファイルをどのように処理するか指示するものです。
vivliostyle.config.js
という名前のファイルを用意して、中身を次のコードとして、JATSBOOK_XML
フォルダー直下に置きます。Qiitaではカーソルをコード領域に持ってくると右上に現れるコピーボタンを押してコピーできます。
module.exports = {
title: 'JATSがわかる 学術情報XML作成の実際',
language: 'ja',
theme: ['https://yamahige.github.io/study/jats2html/vivliostyle/body.css'],
copyAsset: {
includes: [
]
},
author: '学術情報XML推進協議会',
entry: [
// {
// /* 目次 */
// path: "./cover.html",
// theme: ['./body.css', './cover.css'],
// },
{
path: "./2025_i/2025_i.html",
theme: ['https://yamahige.github.io/study/jats2html/vivliostyle/jats_i.css'],
},
{
// path: 'toc-template.html',
theme: ['https://yamahige.github.io/study/jats2html/vivliostyle/toc.css'], // ページ番号を付けるなど
output: 'toc.html',
rel: 'contents',
pageCounterReset: 1,
},
{
path: "./2025_2/2025_2.html",
theme: [
'https://yamahige.github.io/study/jats2html/vivliostyle//body.css',
'https://yamahige.github.io/study/jats2html/vivliostyle/page-reset.css' // ページ番号をリセット
],
},
"./2025_14/2025_14.html",
"./2025_38/2025_38.html",
"./2025_52/2025_52.html",
"./2025_65/2025_65.html",
"./2025_78/2025_78.html",
"./2025_98/2025_98.html",
"./2025_112/2025_112.html",
"./2025_122/2025_122.html",
{
path: "./2025_i/2025_i copy.html",
theme: ['https://yamahige.github.io/study/jats2html/vivliostyle/okuduke.css'],
},
],
output: [
'jatsbook.pdf',
],
toc: {
title: '目次',
htmlPath: 'toc.html',
sectionDepth: 2,
},
workspaceDir: './.vivliostyle',
};
ファイルの構成はこうなります:
JATSBOOK_XML
├── 2025_112
├── 2025_122
├── 2025_14
├── 2025_2
│── 2025_38
├── 2025_52
├── 2025_65
├── 2025_78
├── 2025_98
├── 2025_i
└── vivliostyle.config.js # 追加した構成ファイル
プレビュー
JATSBOOK_XML
フォルダーで、次のコマンドを実行してプレビューします:
$ vivliostyle preview
次のコマンドでjatsbook.pdf
という名前のPDFファイルが出力されます:
$ vivliostyle build
考察
変換といっても、ほとんどの要素はそのままスルーしてます。変換結果を左右見比べると分かるでしょう。article-meta
といった要素もそのままVivliostyleに入力してます。
以降で、変換について主なポイントを説明します。
や 
といった文字実体参照と外部実体
JavaScriptのパーサーには、JATS XML文書をHTML文書text/html
として読み込ませてます。なぜかというと…
や 
といった文字実体参照は、HTMLでは宣言せずに使えます。一方、XMLでは宣言が必要です(実体参照といいます)。で、それはJATSのDTDをたどるとisopub.ent
というファイルで、こんなふうに宣言されてます。
<!ENTITY emsp " " ><!--=em space -->
問題は、JavaScriptのパーサーが、DTDをたどって外部実体を読みに行ってくれないことです。ブラウザーでも、サンプルのJATS XML文書を表示しようとすると次のエラーになります:
This page contains the following errors:
error on line 63 at column 26: Entity 'emsp' not defined
error on line 74 at column 180: Entity 'ldquo' not defined
error on line 79 at column 51: Entity 'nbsp' not defined
error on line 92 at column 57: Entity 'nbsp' not defined
error on line 104 at column 26: Entity 'emsp' not defined
いやいや、「not defined」って、DTD指定してるのに……困りました。
HTML文書として読み込ませれば、この問題は起きません。このために変換後のファイルの拡張子を.html
にしてます。これで大丈夫なのか……
JATS XMLの名前空間
「後方互換のために、JATS自身には名前空間の記法を使わなくてよい」ことになってるようです。J-STAGEとはJATSのバージョンが異なりますが、次のURLに説明があります。
実際、サンプルのJATS XML文書では、XLinkのxlink:href
といった導入要素を除いて、JATS XML自身は名前空間を宣言してません。
一方、HTMLも名前空間を使わなくてよいことになってます。そこで、JATS XML文書をHTML文書として読めてしまう……ようです。
HTMLのtitle
やbody
と事前の文字列変換
JATSにもbody
やtitle
という要素がありますが、HTMLのとは役割が異なります。ブラウザーのDOMParser
や、JSDOM
が読み込む時点で特別扱いされてしまうので、パーサーで読み込む前に文字列変換で名前を変えます。
前記の変換プログラムでは次のように文字列変換してます:
-
title
->jats-title
-
body
->jats-body
title
JATSのtitle
はHTMLのh1
〜h6
相当、あるいは図表のcaption
の中でも使われてます。
こんな感じ
<sec>
<label>(1)</label>
<title>J-STAGE…</title>
<p>…</p>
<!-- … -->
</sec>
あるいはこう
<fig>
<label>図7.2</label>
<caption>
<title>画面構成</title>
</caption>
<graphic xlink:href="gui.jpg"/>
</fig>
一方、HTMLのtitle
は文書中に唯一で特別です。例えば、テキストのみ含めることができて、sup
などを含められません。
また、HTMLのtitle
は空要素ではありませんが、JATS XML文書には空の<title/>
があります。これも事前に文字列変換します。
body
HTMLのbody
要素は特別で、パーサーで読み込むとhtml > body
の配下にhead
以外の要素がぶら下がるように構造変換されてしまいます。
JATSのlabel
HTML + CSSなら疑似要素で自動生成するようなテキストが、JATSではlabel
要素になっています。前記の例もそうなってます。
箇条書きもこんなふうで、「●」が生成済です:
<list>
<list-item>
<label>●</label>
<p>title要素とbody要素は特別です。</p>
</list-item>
<!-- …… -->
</list>
JATSの画像graphic
JATSの画像はgraphic
要素ですが、このままブラウザーなどに渡しても画像は表示されません。変換プログラムでは、パーサーで読み込んだ後、img
要素に置き換えてます。
こんなふうに記述されてます:
<graphic xlink:href="photo.jpg"/>
そこで、次のようなCSSが有効ならよいのですが、url()
関数内にattr()
関数を書けないようで、ダメです。
graphic {
content: url(attr(xlink\:href));
}
/* または */
graphic::after {
content: url(attr(xlink\:href));
}
なお、xml:lang
属性など名前空間プレフィックス付きの属性をCSSの属性セレクターに使うときは、abstract[xml\:lang="ja"]
などと:
を\
でエスケープするそうです。
インラインボックスのまとまり
JATSでは、ブロックボックスにまとめたくなるようなインラインボックスが、特にまとまりなく並んでいます。
セクション見出し
セクション番号と見出しが次のような構造になってます:
<sec>
<label>第1章</label>
<title>JATSの歴史</title>
<p>昔々…</p>
<!-- … -->
</sec>
次のように表示したいのですが、label
とtitle
(jats-title
に変換されます)がp
と同じレベルで並んでいて、まとまってません。
第1章 JATSの歴史
昔々…
このままだと、次の記事の「見出しをrun-inにする」手法を使うことになりますが、扱いにくいので、パーサーで読み込んだ後、jats-title-wrap
要素でくくってます。
<sec>
<jats-title-wrap>
<label>第1章</label>
<jats-title>JATSの歴史</jats-title>
</jats-title-wrap>
<p>昔々…</p>
<!-- … -->
</sec>
図表のキャプションも同様です。「図のキャプションは図の下、表は上」にしたくても、label
、title
、graphic
などと3つに分かれてると、display: flex; flex-direction: column-reverse;
などで制御しようとすると困っちゃいますよね。
<fig>
<label>図7.2</label>
<caption>
<title>画面構成</title>
</caption>
<graphic xlink:href="gui.jpg"/>
</fig>
目次生成
Vivliostyleに目次を自動生成してもらうために、title
要素(というかjats-title-wrap
要素)のままでは目次ができません。h1
、h2
、h3
などの要素を想定しているからです。そこで、トップレベルだけjats-title-wrap
をh2
に置き換えてます。
奥付
奥付を生成するために、どれかのHTMLファイルを複製します。前記の手順では2025_i copy.html
としました。中身は全く同じでファイル名が異なるようにします4。どのファイルでもよいです……つまり、どのファイルにも(章だけでなく)書籍の情報が入ってます。論文でいえば掲載誌の情報です。今回は表紙を作ってませんが、表紙も同じ方法で作れます。
もちろん、DOMを変換して奥付用ファイルを作るワークフローもありでしょう。
画像graphic
のサイズ
さて、入手できるJATS XML文書では、画像にサイズがありません。
そこで、Vivliostyleに渡すCSSでは、graphic
から変換したimg
について一律に、次のように設定してます:
img {
max-inline-size: 90%;
max-block-size: 60vh;
margin-inline: auto;
}
個々の画像にサイズを設定してみよう
これはページ組版なので、サイズや配置を調整したいですよね。
やってみましょう!
まず、できれば次の記事のように、Visual Studio Code (以下、VSCode)とXML拡張機能(vscode-xml)を用意して、DTDを読み込んでバリデーションできるようにしておきます。できなくてもよいです、テキストエディターでもOKです。
JATS XML文書、例えば./2025_2/2025_2.xml
(2025_2.html
ではなく)をVSCodeで開いて、graphic
要素を、style
属性付きのstyled-content
要素で囲みます。
<graphic xlink:href="2025_2_f1.jpg"/>
を、このように編集します。
<styled-content style="inline-size: 50%; float: outside;">
<graphic xlink:href="2025_2_f1.jpg"/>
</styled-content>
画像の幅をページ幅の50%にして外側寄せにするという設定です。
float: outside;
はVivliostyleの新しい機能です
編集前は横幅いっぱいだった画像が、半分の幅で外側寄せになってれば成功です。
このようにして、ソースのXML文書で、画像のサイズや配置を調整できます。
styled-content
要素とstyle
属性
ここで、編集後でも妥当なXML文書であることに注目してください。VSCodeとvscode-xmlを使って編集すると分かります。バリデーションが成功します。
試しに、graphic
要素にstyle
属性を設定するとエラーになり、ちゃんとバリデートされてることが分かります。style
のところに赤い波線が表示されるのを確認できます。
<graphic xlink:href="2025_2_f1.jpg" style="inline-size: 50%; float: outside;"/>
styled-content
要素にstyle
属性(XMLでは@style
と表現したりします)を使うのは、JATSが用意している方法です。
ただし、J-STAGEの「XMLフォーマットガイドライン」などにはstyled-content
の記述が見当たらず、使ってよいものかどうか分かりません……
この他に、named-content
要素があって、こちらはHTMLのclass
属性の用法に似ています。ただし@class
はなくて、@content-type
や@specific-use
で識別します。
JATSの画像のサイズ設定
JATSで画像にサイズが設定されていなくてもよい事情は、実際に公開されている論文をJ-STAGEで見ると想像できます。どの画像も固定幅の100%で表示するように設定されていて、クリックすると拡大表示されるようになってます。
次のようにして実物を確認できます:
J-STAGEトップページで、「検索条件の詳細設定」から、まず「指定検索」に何か入力します(ここに何か入れないと検索してくれません)。そして「記事属性」で「本文(HTML形式)」をチェックします。見たところ、HTML形式が公開されてるのは医薬系に多いです。例えば「論文タイトル」に「COVID-19」などと入力します。検索結果一覧の画面で、「認証」の「フリー」や「オープンアクセス」をチェックすると無料・会員登録不要で読める論文に絞り込めます5
あるいは「情報管理」という雑誌です。休刊してるのですが、この論文は、すべてフリー& HTML形式で見られるんじゃないでしょうか
https://www.jstage.jst.go.jp/browse/johokanri/-char/ja
いずれも論文一覧の画面で、各論文の「HTML形式で全画面表示」をクリックするか、タイトルをクリックして開いたページの右上の「本文(HTML形式)」をクリックすると、JATS XMLから変換して作ったHTML形式で論文を閲覧できます。
細かいところはやってません
- 著者は奥付に一覧したい。そういった奥付生成はDOMで可能でしょう
- 図のキャプションは下、表キャプションは上にしたい
- 目次の最後の項目の見出しが「序文…」なのはおかしい。そもそも奥付は目次に載せないでしょう
- いまさらですが、XML文書の空白文字や改行は要注意で、整形(prettify)は危険です。何がどのように変換されるのか、整形した方が対比しやすいので、変換プログラムは整形して見せています。実際のワークフローでは整形しないデータを使うべきです。(2025-07-27 追記)
以上です、お疲れ様でした。
CSSの中身は、後でゆっくりご笑覧ください。ここで紹介した手法を使ってます
-
Darwin Information Typing Architecture https://ja.wikipedia.org/wiki/Darwin_Information_Typing_Architecture ↩
-
同じファイルが複数個entryしてるとVivliostyleでエラーになります。 ↩
-
「COVID-19」の結果はフリーやオープンアクセスのものだけですね……という点が、JATS XMLについては重要だったりします。 ↩