「Paged.js を試す」 @akabekobeko のレビュー
「Paged.js を試す」 @akabekobeko
https://akabeko.me/blog/2020/10/pagedjs/
Vivliostyle サンプル
- V: OK
- P: NG、ルビ表示 OK。ページ処理無効。後続ページであろうテキストが重なって表示される。
このサンプルではルート要素 <html>
に column-count: 2
で段組が指定されています:
https://github.com/vivliostyle/vivliostyle_doc/blob/a8580f1175ff84ecee1497681f5c78b28a9a78c7/samples/fixed-page-size/ja-horizontal/Takasebune/style.css#L56-L61
html {
/* 段数 */
column-count: 2;
/* 段間 */
column-gap: 2em;
}
Paged.js は <html>
や <body>
に段組が指定されていると処理できないという問題があります: https://gitlab.pagedmedia.org/tools/pagedjs/issues/134
(逆に Vivliostyle は <html>
や <body>
に指定された段組しか正しくページ分割処理ができません: vivliostyle/vivliostyle.js#579 )
Paged.js でのこの問題の回避策は <html>
や <body>
に段組を指定しないで、<body>
の中の <div>
要素などに段組を指定することです(VivliostyleではNGになります)。その説明がissueのコメントにあります:
https://gitlab.pagedmedia.org/tools/pagedjs/issues/134#note_854
- V: OK
- P: NG、ページ処理無効。後続ページであろうテキストが重なって表示される。
1と同じ。
Alice's Adventures in Wonderland
- V: OK
- P: NG、左ノンブル表示無効、リスト連番無効、
first-letter
無効など。コンテンツの区切り?に十字の記号が表示されるのは独自拡張?
「左ノンブル表示無効」?
「左ノンブル表示無効」は違います。空白ページ以外の左ページのノンブルは表示されています。空白ページ(break-before: right
によって左ページが空白ページになる)のノンブルが表示されていないのは、CSSで指定されているとおりであり、Paged.jsのほうが正しいです。
https://github.com/vivliostyle/vivliostyle_doc/blob/a8580f1175ff84ecee1497681f5c78b28a9a78c7/samples/gutenberg/gutenberg.css#L37-L41
@page :blank {
@bottom-left { content: normal; }
@bottom-right { content: normal; }
@top-center { content: normal; }
}
Vivliostyleは空白ページセレクタ :blank
が未サポートです: vivliostyle/vivliostyle.js#428
そのため、このCSSルールが無視されて、空白ページにもノンブルが表示されます。
したがってノンブルの表示については Vivliostyle が NG (制限あり)で Paged.js のほうが OK です。
「リスト連番無効」
Paged.jsのバグのようです。次のCSSルールの counter-increment: chap;
が無視されているようです:
nav li::before {
counter-increment: chap;
content: counter(chap, upper-roman) ".";
float: left;
width: 3em;
margin-left: -3em;
}
擬似要素(::before
)に指定されている counter-increment
が無視されます。
counter-increment
を擬似要素ではなく次のように普通の要素に指定するようにすることでこの問題は回避できます:
nav li {
counter-increment: chap;
}
nav li::before {
content: counter(chap, upper-roman) ".";
...
}
Paged.jsのissuesに該当するものは見つかりませんでした。
first-letter
無効」
「Paged.jsのバグのようです。
:first-of-type
擬似クラスと一緒に指定されている ::first-letter
擬似要素が無視されています(::first-letter
だけの指定なら問題ないことを確認済み)。
p:first-of-type::first-letter {
font-weight: bold;
font-size: 1.8em;
line-height: 1;
padding-right: 1px
}
Paged.jsの次のissueが関係しそうです:
"CSS selectors like :nth-of-type(x) on a section"
https://gitlab.pagedmedia.org/tools/pagedjs/issues/13
コンテンツの区切り?に十字の記号
Paged.jsの結果は正しくありません。
この十字の記号は、次のCSSルールにより生成されています: https://github.com/vivliostyle/vivliostyle_doc/blob/a8580f1175ff84ecee1497681f5c78b28a9a78c7/samples/gutenberg/gutenberg.css#L161-L172
main > section:last-child::after {
break-before: avoid;
break-inside: avoid;
margin: 2em 0;
text-align: center;
font-size: 2em;
font-weight: bold;
font-variant: small-caps;
content: "\a\002722";
white-space: pre;
display: block;
}
Alice.html 内に main
要素はひとつしかなく、その main
要素内の最後の子要素である section
要素の終わりに十字の記号を入れるものです。
Vivliostyleの結果では、正しく最後のセクションの終わりにだけ十字の記号が入っています。
それに対して、Paged.jsでは十字の記号が繰り返し出現しています。
VivliostyleもPaged.jsも、ページ分割の処理で、もとはひとつの要素を複数の要素に分けて、ページの領域を表す div
要素の中に入れるという処理をしているのは同じです。
main > section:last-child
のようなCSSセレクタは、もとのHTML構造のためのものなので、ページ分割処理後のDOMにそれを適用すると、正しい結果にならないのは当然です。だからVivliostyleでは、CSSセレクタの解釈とカスケーディング処理を自前でやっていて、この問題が起きないようにしています。Paged.jsは、それをやっていないので、ページ分割処理後のDOMを意識したスタイルシートの書き方をしないと、正しい結果が得られません。
- V: OK
- P: NG、左ノンブル表示無効、リスト連番無効、
first-letter
無効など。コンテンツの区切り?に十字の記号が表示されるのは独自拡張?
3と同じ。
The Adventures of Sherlock Holmes
- V: OK
- P: NG、PDF 出力不能。詳しくはこちらを参照のこと。
Paged.jsのissueを検索したところ、すでにこのバグは登録されていました:
- Use of querySelectorEscape misguided https://gitlab.pagedmedia.org/tools/pagedjs/issues/208
この問題は、数字ではじまるIDが target-counter()
で参照されていることで発生しています。
(単にHTML内に数字ではじまるIDがある場合やそれが内部リンクに使われているだけなら発生せず、ページ番号を付加するために target-counter()
が使われているときに発生するものでした。)
CSSとHTMLは次のようになっています:
nav li a::after {
text-align: right;
content: target-counter(attr(href, url), page);
<li><a href="#1">A Scandal in Bohemia</a>
<li><a href="#2">The Red-Headed League</a>
<li><a href="#3">A Case of Identity</a>
- V: OK
- P: NG、ページ処理無効。後続ページであろうテキストが重なって表示される。
1と同じ。
- V: OK
- P: NG、ページ処理無効。後続ページであろうテキストが重なって表示される。
1と同じ。
- V: OK
- P: OK
- V: OK
- P: NG、ページ処理無効。「高瀬舟」などと異なり後続ページのテキストは出現せず重ならない。
成否の数を表へまとめる。
Software OK NG Vivliostyle 9 0 Paged.js 1 8
複数のテストサンプルで同じ問題が起きているので、問題別にまとめたほうが意味があるでしょう:
機能 | Vivliostyle | Paged.js |
---|---|---|
ルートまたはbody要素での段組 | OK | NG |
空白ページでノンブル非表示 | NG | OK |
before擬似要素でのcounter-increment | OK | NG |
p:first-of-type::first-letter | OK | NG |
section:last-child::after | OK | NG |
print-css.rocks サンプル
- V: NG、"Box with text overflow" だけ OK、それ以外は青枠に収まらずはみ出る。
- P: NG、同上。
このテストは antennahouse 専用です: https://github.com/zopyx/print-css-rocks/blob/bd2e6e82aa1a19ee53a6b11474cd793642209614/lessons/lesson-antennahouse-autofit-text/Makefile#L1
all: antennahouse
AH独自拡張である -ah-overflow-condense
プロパティをテストするものです:
https://github.com/zopyx/print-css-rocks/blob/bd2e6e82aa1a19ee53a6b11474cd793642209614/lessons/lesson-antennahouse-autofit-text/styles.css#L18
-ah-overflow-condense: font-stretch;
結果はNGではなくNAとするべきでしょう。
antennahouse-multicol-with-sidenotes
- V: NG、赤い傍注が上部に表示、他の傍注は本文へ重なる。テーブルに本文テキストが回り込む。
- P: NG、赤い傍注は正常表示、他の傍注は消える。テーブルに本文テキストが回り込む。
このテストも antennahouse 専用です:
このテストは Antenna House の独自拡張である -ah-float: outside
などをテストするものです。
結果はNGではなくNAとするべきでしょう。
- V: NG、画像が表示されない。
- P: NG、同上。
このテストは PDF/A の規格に適合するかどうかを評価するためのものです。
https://github.com/zopyx/print-css-rocks/blob/master/lessons/lesson-archive-pdf/Makefile
prince:
prince --tagged-pdf --pdf-profile 'PDF/A-3b' index.html --tagged-pdf -o prince.pdf
antennahouse:
run.sh -d index.html -pdfver PDF1.7/A-3b:2012 -i config.xml >antennahouse.pdf
pdfreactor:
pdfreactor.py --conformance PDFA3B -i index.html -o pdfreactor.pdf
このように PDF/A 出力をサポートするエンジンでは、コマンドラインでオプションを指定することにより PDF/A 出力が可能です。
Vivliostyle や Paged.js にはこの機能がないので、テストすることはできません。
「画像が表示されない。」というのは、 https://github.com/zopyx/print-css-rocks/blob/bd2e6e82aa1a19ee53a6b11474cd793642209614/lessons/lesson-archive-pdf/index.html#L41
<img src="kangaroo.png" alt="This is a kangaroo"/>
この画像ファイルが同じディレクトリに存在しないためです。このテストをするときに kangaroo.png をコピーする必要があるでしょう。
- V: OK
- P: NG、スケーリングとマージンが効かずページ全体に画像が広がる
https://github.com/zopyx/print-css-rocks/blob/master/lessons/lesson-background-repeated/styles.css
body {
background-image: url(back.jpg);
background-repeat: repeat-y;
}
このように HTML body
要素に指定された background プロパティは、CSS Backgrounds仕様により、ルート要素に指定されたのと同様に canvas background となります。
そして、CSS Paged Media仕様 で、"The document canvas background is drawn as the page box’s background: by default its background painting area covers the page box’s border box" と定められています。"covers page box’s border box" ということなので page box の margin の部分はカバーされないのが正しいはずです。
Paged.js の制限事項といえると思います。
Paged.jsはページ分割処理で元のHTML文書から構造を変形させたDOM構造を生成します。
元のHTML文書の構造:
html
body
h1
p
Paged.jsでページ分割処理されたDOM構造:
html
body
div.pagedjs_pages
div.pagedjs_page ※ 複数ページではこれを繰り返し
div.pagedjs_sheet
div.pagedjs_pagebox ※ page-margin boxesもこの中に生成
div.pagedjs_area
div.pagedjs_page_content
div
h1
p
このように構造が変わってもルート要素やbody要素に指定されたプロパティはそのまま適用されるのでpage-margin部分も含む全体をカバーするbackgroundになる。
- V: OK
- P: OK
- V: NG、ブランク ページは処理できているが他のソフトウェアのように青ではなく真っ白になる
- P: OK
Vivliostyleは空白ページセレクタ :blank
未サポート: vivliostyle/vivliostyle.js#428
- V: OK
- P: NG、p2 のボーダーが正方形ではなく縦へかなり延びた長方形になる
https://github.com/zopyx/print-css-rocks/blob/master/lessons/lesson-border-model/index.html#L32
<div class="container" id="large-border">
https://github.com/zopyx/print-css-rocks/blob/master/lessons/lesson-border-model/styles.css
.container {
margin-bottom: 1em;
width: 50%;
background: #ddd;
outline: 1px dashed green;
}
...
#large-border {
padding: 30px;
border: 20px solid pink;
margin: 50px;
}
このスタイルシートで <div class="container" id="large-border">
の要素は width: 50%
であるが、これは内容の幅のはずであり、padding や border があることで内容の幅が小さくなるべきではない。しかし Paged.js の結果は、あたかも box-sizing: border-box
の指定があるかのように、内容の幅が小さくなっている。
pagedjs-cli で組版処理結果のHTMLを出力(-x
オプション)して調べると、実際に box-sizing: border-box
が適用されている。
Paged.jsのソースコードの次のところにこれがある:
https://gitlab.pagedmedia.org/tools/pagedjs/blob/master/src/polisher/base.js#L262-264
.pagedjs_pagebox * {
box-sizing: border-box;
}
これは意図的なのかもしれないが、CSS仕様の標準と合っていないので問題です。
Paged.jsのissuesに該当するものは見つかりませんでした。
- V: OK
- P: OK
- V: OK
- P: OK
- V: OK
- P: OK、ネスト表示を対象としているためか Lessons で OK とされているが Paged.js だけ数字が 1 開始ではなく 2 となっているのは気になる。
Paged.js の結果は OK ではないでしょう。h1 (Chapter Level 1) のカウンターが 2, 4, 6 のように2ずつインクリメントしています。
h1 {
counter-increment: c1;
counter-reset: c3;
counter-reset: c2;
}
Paged.jsの動作は、あたかも counter-increment: c1 2;
で2ずつインクリメントしているようです。
このCSSで気になるのは、ひとつのスタイルルールの中で counter-reset
プロパティが2つ書かれていることです。CSSの規則によりあとのほう counter-reset: c2;
だけが効果をもつはずです。
しかし、試しにこの無意味なはずの counter-reset: c3;
を削除してみると、カウンターc1が2ずつインクリメントする現象は起きず、正常に1ずつインクリメントします。
- V: NG、チャート表示されず真っ白になる。
- P: NG、同上。
Vivliostyleは元HTML文書内の <script>
要素を無視するので JavaScript で表示するチャートがでません。
Paged.jsでは無視することはないはず。なぜ表示されないか不明。
pagedjs-cli で組版処理結果のHTMLを出力(-x
オプション)して、それをChromeブラウザで開くとチャートは表示されるしブラウザkらのPDF出力でも正常に出力される。
- V: OK、Lessons 上で OK とされる Paged.js 相当の表示になっているため OK とした。しかし Paged.js と共に他サンプルで表示されている冒頭テキストの背景と枠線がない点が気になる。
- P: OK
VivliostyleもPaged.jsもCMYKカラーに非対応なのでこのテストの結果がOKというのは間違いだと思います。
(Vivliostyle CLIは --press-ready
オプションでCMYKカラーのPDFを出力できますが、これはRGBで出力されたものCMYKに変換しているので、もとのCMYKの画像やCMYKでの色の指定が反映されるわけでありません)
https://github.com/zopyx/print-css-rocks/blob/master/lessons/lesson-cmyk/styles.css
#cmyk {
border: cmyk(0.5, 0.1, 0.0, 0.2) solid thick;
background: cmyk(0, 0.3, 0.2, 0);
color: cmyk(0.8, 0.5, 0.0, 0.3);
}
現在のCSS Color Level 4仕様 では cmyk()
ではなく device-cmyk()
が定義されています。cmyk()
は古いドラフト仕様での名前で、AH Formatterなどではどちらの名前でも動きます。
- V: OK
- P: OK
- V: OK
- P: NG、目次の Chapter が対応する I 〜 IV ではなく全て 0。
Paged.jsのissueにこのバグが登録されていました: https://gitlab.pagedmedia.org/tools/pagedjs/issues/122
とりあえず、今日はここまで。
まだ途中ですが、VivliostyleとPaged.jsの違いはだいぶ見えてきたと思います。
-
Vivliostyleはルート要素またはbody要素に指定された段組は、自前で段組の処理をする(ブラウザが実装しているCSS段組機能を使わない)が、body内の要素に指定された段組はブラウザのCSS段組機能で段組になる。しかしそのページ分割処理がVivliostyleではできていないのが残念。 Paged.jsではルート要素またはbody要素に指定された段組は正しく機能しないが、body内の要素に指定された段組は機能する(ブラウザのCSS段組機能も利用しながらページ分割処理も行われている)。
-
Vivliostyleは(Peter SorotokinのAdaptive Layout実装のおかげで)、自前のCSSパーサーとカスケーディング処理で、HTMLの各要素のCSSプロパティの値を得て、各要素のstyleを確定させている。そのため、ページ分割処理で要素が分割されて構造が変わっても、CSSセレクタで選択される要素が別のものに変わったりはしない。 それに対してPaged.jsは自前でのCSSの処理は最小限にしている。それによりブラウザのCSSの機能をそのまま生かせるメリットがある(例えばCSS Variablesが使える)一方、ページ分割処理でDOM構造が変わることでスタイルシートでのCSSセレクタが意図通りに機能しないことがある。
-
CSS counterの機能は、pageカウンターやtarget-counter()など、ブラウザのCSS処理では足りないものなので、Vivliostyle、Paged.jsともに独自の処理が行われているが、Paged.jsはこの部分の不具合が多い。
-
Vivliostyleは空白ページセレクタ
:blank
未サポートなのが残念。それから(ここまでのテストでは出てこないが)、named strings や named pages など、Paged.jsや他の CSS Paged Media 対応エンジンで当然のように実装されている機能が未実装のままなのが残念。