カテゴリー別アーカイブ: XSL-FO・CSS

[XSL-FO試行錯誤]索引のページ番号表示で一部を太字にする

索引語に続くページ番号表示。「3,6,11-15,100,…」のように並んでいても、「どこがその語を説明している箇所なのか」は分かりません。そのようなときに、索引表現ではとくに重要・説明的である箇所のページ番号についてスタイルを変えて表現することがあります。これを実現するFOについて考えてみましょう。


「ある箇所について特別な処理をしたい」というとき、必要なのは次の情報です。

  • 「ある箇所」と他を区別するルール
  • 特別な処理を実現するFO

今回の場合、「通常の索引と、重要な1箇所の索引が区別できること」については元のXMLとXSLT処理の書き方によって
大きく異なると思われますので、それらを最終的に落とし込むFOについて検討します。

索引用FOの機能

索引用のFOの基本は、@index-keyによって指定されるキーと、そのキーを表示するための<fo:index-citation-reference>です。

索引 XSL-FOの基礎 第2版

これらのFOでは、該当するキーを含むページ番号について、FOプロセッサが列挙し表示を調整するという処理を行います。
つまり、FOプロセッサというユーザの手を離れた段階で処理されるので、「索引用のFOで一部のページ番号だけを太字にしたい」という要求を実現するには、このFOでは上手くいかなさそうだということを意味します。


索引用FOでは「2ページだけを太字にする」処理は難しい

索引にリンクを付けたい場合はfo:index-citation-list/@page-number-treatment="link"で表示されたページ番号にリンクが付与されます。

汎用のページ番号参照

あるページの参照だけであれば、fo:page-number-citaionに該当箇所のidを指定すれば実現できます。これを利用することにします。
制限として、数ページにわたるページ番号参照には対応できません。

汎用的なリンク用のFOであるfo:basic-linkでは、internal-destinationにidを指定することで内部リンクを付与できます。

よって、id情報があれば、該当の番号のページへジャンプするリンクが付与されたページ番号参照が実現できます。

最終的な方針とFO

重要な箇所の索引をspとします。通常の索引のときは、fo:wrapper/@index-keyを挿入する通常の索引用の属性を、spの索引のときはfo:wrapper/@idを本文に挿入します。このとき、spの前後でindex-keyの値は変えておきます。

本文のFOを通常の索引とspとで切り換える
<fo:block><fo:wrapper index-key="あんてなはうす1"/>アンテナハウスは、Data Usability Companyです。</fo:block>
<!-- spの索引語 -->
<fo:block ...><fo:wrapper id="antenna"/>アンテナハウスは、日本のソフトウェア企業で、XML自動組版ソフトウェアAntenna House Formatterや、PDFに関連した製品を開発・販売しています。</fo:block>
<fo:block ...><fo:wrapper index-key="あんてなはうす2"/></fo:block>
<fo:block ...><fo:wrapper index-key="あんてなはうす2"/></fo:block>

索引ページのFO
<fo:block ... space-after="2rem" font-size="72pt">索引</fo:block>
  <fo:block background-color="black" color="white">あ</fo:block>
  <fo:block text-align-last="justify" text-align="justify">アンテナハウス<fo:leader leader-pattern="dots" leader-alignment="center"/><!--
--><fo:index-page-citation-list merge-sequential-page-numbers="merge"
	merge-pages-across-index-key-references="merge" 
	merge-ranges-across-index-key-references="merge">
	<fo:index-page-citation-list-separator>,</fo:index-page-citation-list-separator>
	<fo:index-page-citation-range-separator>-</fo:index-page-citation-range-separator>
	<fo:index-key-reference ref-index-key="あんてなはうす1" page-number-treatment="link" />
</fo:index-page-citation-list><!--spの索引--><fo:inline>,<fo:inline font-weight="bolder"><fo:basic-link
 internal-destination="antenna"><fo:page-number-citation ref-id="antenna"/></fo:basic-link></fo:inline>,</fo:inline><fo:index-page-citation-list
  merge-sequential-page-numbers="merge"
  merge-pages-across-index-key-references="merge" 
  merge-ranges-across-index-key-references="merge">
	<fo:index-page-citation-list-separator>,
	<fo:index-page-citation-range-separator>-
	<fo:index-key-reference ref-index-key="あんてなはうす2"  page-number-treatment="link"/>
</fo:index-page-citation-list>
</fo:block>

fo:index-page-citation-listは複数のfo:index-page-referenceを持てますが、今回は特別扱いするfo:inlineを挟みたいため、一旦終了して特別処理の箇所の後に別のfo:index-page-citation-listを開始しています。

これをFormatterで組版し、冒頭に上げた画像の索引が作成できました。

補足

実作業で厄介なのはXSLTでの処理時に@index-keyを持たないspを、索引リスト作成時にどのようにして取得するかでしょうか。また、spである索引が登場する場合としない場合、sp前後に同じ語の索引が登場しない場合の「,」表示の分岐など、索引FOが吸収していた面倒な分岐も自前で用意する必要があるでしょう。

索引ウェビナーの宣伝

索引について、もう少し全般的な紹介をするウェビナーを2023年9月19,26日(火)16時に行います。今回の記事は応用実装よりでニッチ度が高いものでしたが、まだまだ話題がありますので、どうぞご検討ください。


[CSS組版]ページ番号のリセット

今回紹介するのは「表紙を同一のHTMLから生成するとき、
表紙のページ番号はカウントしたくない」という要望の実現についてです。誤りがあれば、ご指摘いただければ幸いです。

組版にはAntenna House CSS Formatter V7.3MR1を利用しています。スクリーンショット画像の、ページ番号以外の本文内容について画像によって差異がありますが、記事を作りながら都度組版をしていたためで、本題には支障ありません。

CSSページ組版でページ番号を参照するには、content: counter(page)と指定します。

リセット未指定でのページ番号カウントを見てみましょう。

@page {
    size: JIS-B5 portrait;
    margin: 2cm;
    @bottom-center {
        content: counter(page);
        font-size: 24pt;
        font-variant: oldstyle-nums;
    }
}

@pageルールで全体のページレイアウトを設定しています。@bottom-centerでページ番号を表示するように指定していますね。

1枚目のページに「1」、2枚目のページに「2」が振られています。

pageカウンタをリセット

1枚目を表紙ページとして、2枚目のページに「1」が振られるようにしてみましょう。

ところでCSSのカウンタはページ組版に限らない汎用的な仕組みです。pageはページ組版のとき、定義済みのカウンタ名として存在している状態です。
つまり、カウンタリセットの仕組みを使うことで、ページ番号についてもリセットが可能です。

... {
      counter-reset: page 1;
     }

できることが分かったところで、次の問題、すなわち「どのセレクタ(要素・ルール)にリセットを記述するか」に移ります。

今回は「表紙ページ」と「それ以外のページ」という分け方ができます。「表紙用のページレイアウト」、
「本文用のページレイアウト」が用意できると嬉しいわけです。@pageルールでは、そういう処理が書けます。

@page {...}
@page mainmatter {
  counter-reset: page 1;     
}
#cover {
    break-after: always;  
}
div#mainmatter {
    page: mainmatter;
}
<div id="cover">
    <h1>[CSS組版]ページ番号のリセット</h1>
    <p>今回紹介するのは...</p>
</div>
<div id="mainmatter">
    <p>CSSページ組版でページ番号を参照するには、...</p>
</div>

上手くいった……と油断するのはまだ早いです。このままでは、@page mainmatterの対象ページが切り換わる度にpageは1にリセットされるのです。

リセット対象のページを追加で条件付けすることで解決できます。@page mainmatter:firstを追加することで、「mainamtterページレイアウトが使用される最初のページでpageを1にリセットする」という処理になります。

 @page mainmatter:first {
    counter-reset: page 1;     
}

期待する出力になりました。

表紙のページレイアウトからページ番号を削除する

ついでに、表紙ページからはページ番号表示を外しておきます。

@page cover {
    @bottom-center {
        /*bottom-centerのコンテンツを上書き*/
        content:"";
    }
}
#cover {
    /*coverページレイアウトを適用*/
    page: cover;
    break-after:always;
}

参考


XSL-FO 試行錯誤 SVG 2.0 inline-sizeのTips

2023年01月19日に『Antenna House Formatter V7.3』がリリースされました!
関連して、2023年01月31日(火)16:00-17:00 にZoomウェビナーとして『Antenna House Formatter V7.3 リリース!  新機能と利用シーン紹介』を開催します。


https://us06web.zoom.us/webinar/register/4716717860524/WN_ElB-ZrzFQzShzKGqWZMHqg

新機能紹介ということで、XSL-FOやCSSページ組版の基礎知識があることを前提としています。しかし、特に知識の無い方でも「自動組版でこんな細かいレイアウト調整が効くのか」と楽しめることを目指していますので、お時間が合いましたら是非参加登録いただければ幸いです。

ウェビナースライドはFormatter V7.3で組版しています。スライドはCSSで組んでいるのですが、話題がSVGについてなので、暫くぶりとなる「XSL-FO 試行錯誤」シリーズを題に含めました。
(前回のXSL-FO試行錯誤のカレンダー作成の続きについてはまた時間があるときにさせていただければ……)

さて、本記事ではウェビナーで割愛する部分について、先行して補足します。
ということで、見た目が地味なのであまり深く掘り下げない予定のinline-sizeによるテキストの折り返しについてです。

発表スライドの見出し部分にはSVG 2.0を使っています。

テキストを配置するとき、SVG 1.xでは折り返しを扱えませんでした。
よって、複数行のテキストを配置するときは次の手順(これ以外にもなくはないですが)です。

右揃えで3行でテキストを描画するとします。座標は適当です。

  1. 改行位置でテキストを分割し、別のtspanに分ける
  2. それぞれのtspanにx,yの座標を指定する。2行目であれば1行目から1文字分以上離れた位置にyを設定する
<!-- SVG 1.x -->
<text x="0" y="0">
<tspan text-anchor="end" x="250" y="0">Formatter V7.3</tspan>
<tpsan text-anchor="end"  x="250" y="10">リリース!</tspan>
<tspan text-anchor="end"  x="250" y="20">新機能と利用シーン</tspan>
<text>

text-anchor=”end”によって、xの位置がテキストの終端になっています。

x,yはSVG内の座標系なので、font-size:18ptのように絶対単位で指定されていると対応する高さを調整するのはちょっと手間ですね。
他にも拡大縮小の際などにこれを忘れずに変更しなければなりません。

メリット、といえるかは分かりませんが、あらかじめ分割しているので、意図しない行分割が発生するということはないでしょう。

SVG 2.0のテキスト折り返し機能を使えば、折り返しを自動化可能です。Formatter V7.3ではinline-sizeによる折り返しをサポートしています。
Scalable Vector Graphics (SVG) 2 11.4.1. The ‘inline-size’ property



<text x="0" y="0" inline-size="1000">Formatter V7.3リリース! 新機能と利用シーン</text>

inline-sizeにはテキストの行長を指定します。

  • 両端揃えができない
  • 日本語は行頭・行末禁則以外では基本的に改行可能と判断される

また、見出しのような箇所で使う場合、次のことを意識しておくとよいでしょう。

今まで手動でやっていた行分割を自動処理にする都合上、特に、本文でない箇所での行分割規則は意識しておかなければなりません。
言語処理が日本語になっていれば(xml:lang=”ja”が適用されるような箇所であれば)
意図してそうしない限り「ー」は先頭に来ませんが、「リ」はそうではないので、

Formatter V7.3 リ
リース!

となるかもしれません。

特定単語の行分割を防ぐため、その箇所のマークアップをtspanにして、分割禁止のプロパティを付けることにします。
「tspanを使うなら結局SVG 1.xとあまり変わらない?」と思われるかもしれませんが、座標の明示がなくなるので処理はかなり単純化します。
「この単語で行分割されるのは困るが、ここ以外であれば別に構わない」というケースが多いとみているのですが、どうでしょうか。

<!-- <heading>Formatter V7.3<keep>リリース!</keep><keep>新機能</keep>と<keep>利用シーン</keep></heading> -->
...
<xsl:template match="heading">
  <svg:svg viewBox="0 0 1920 1080" >
  ...
    <svg:style>
      svg|tspan.keep {
        word-break: keep-all;
      }
    </svg:style>

    <svg:text x="1800" y="100" text-anchor="end"><xsl:apply-templates mode="#current"/></svg:text>
  </svg:svg>
</xsl:template>
...

<xsl:template match="keep">
  <svg:tspan class="keep"><xsl:apply-templates mode="#current"/></svg:tspan>
</xsl:template>

上記は実際に使用したコードではないため、参考程度にお考えください。
inline-sizeが対応するalign調整はtext-anchorによるものになるため、両端揃えの自動配置はできません。

その他、SVGのテキストを使う場合の注意点として、FOのブロックやHTMLのテキストと異なり、
「viewBoxは伸長しないため、想定よりテキストが長くなると表示が見切れる」という点があります。
つまり、「ページ単位で表示領域を確保できる」など、特に高さ・テキスト長があらかじめ想定できる
範囲で使うようにするとよいでしょう。

そして、Formatter V7.3で対応したSVGフィルタ・マスク機能と組み合わせることで文字列に効果を付加できます!

SVGテキストとマスク

画像に対し、テキストをマスクとして被せたものになります。このSVGではtext-anchor=”middle”です。
実のところ、SVGマスク以外の方法もあります、ただ、SVGマスクやフィルタは汎用的ですし、追加の独自仕様無しでXSL-FOでも使えるというメリットがあります。


【動画公開】XSLT超入門3, CSS組版スライドと補足

ちょっと一息アンテナハウスウェビナー「XSLT超入門 3 XPathについて」を終えました。ご視聴いただいた方、ありがとうございます。お時間の合わなかった方、動画が公開されたので、ぜひご覧ください。


Zoomの注釈機能

発表直前に気付いたのですが、Zoomの「画面の共有」ボタンの横に、いつの間にやら「注釈」というボタンが増えていたので早速使ってみました。いかがでしたでしょうか。

PowerPointスライドと違い(おそらくほとんどの)PDFビューアにはレーザポインタ機能がないので、助かりますね。(他機能として、テキストも表示できます。スライド表示中に「これ補足した方がいいかな」といった思いつきでテキストを追加したりはPDF注釈でも実現できますが、操作の切り換えの手間があるため、Zoomにあるに越したことはない、くらいの感想です。)

CSS組版スライド

担当したここ数回のウェビナーではPowerPointでスライドを作成していました。今回も途中まではそうだったのですが、内容の大幅な修正を行うついでにAH FormatterでHTML+CSS組版によるPDFスライドに移行しました。

以前XSL-FOでスライドを組版したことを紹介しました。スライド発表程度の長さであれば単一HTMLでもあまりテキスト分量はかさみません。今回、HTMLが650行弱、CSSは240行程度でした。実作業としてはSCSSで記述して変換していたので……240行程度でした。まあ、セレクタ部分は簡潔になりますが、改行・インデントとしてはあまり変わらないんですね。CSSについてはAH CSS Formatterの既定のCSSを読み込んだ上でなので、0から組むのであればもう少し嵩むでしょう。

VS CodeでSCSSを編集 with AH CSS Extension

SCSSの編集はVS Codeで行いました。VS CodeでのCSSの補完・サジェストについては、@media printなども最初からある程度対応しています。
加えて先日、AH CSS Formatterの豊富な拡張仕様を入力補助してくれるAH CSS Formatter Extensionが公開されたので早速使っています。SCSSファイル編集でも有効なようです。

https://github.com/AntennaHouse/ahformatter-vscode-css-ja

「HTMLソースでスライドを作成する」というと、気になるのは「発表スライドとWebページを同時作成できるか?」という点ではないでしょうか。結論としては「内容次第」ということに落ち着いてしまうのですが、「ある程度まで同一ソースで作成する」ことを目標とすると、方針として押さえるべき点は次のことでしょう。

  • HTMLソースはWebページ向けが主、スライドは従
    • スライド組版時はdisplay:noneにする情報を決めておく
    • スライド向けCSSを先行する場合、とくに注意するのはfloat配置とheightの100%

もちろん、広告を目的とするWebページなどで一度に入る情報量を絞ることなどはよくあります。ここではある程度技術的内容のプレゼンとそのWebページ化を前提としています。

スマホ向けとPC・タブレット向けのページを同じソースで作成する場合にスマホ向けをメインにレイアウトすることを「モバイル・ファースト」といったりしますが、Webページとプレゼンテーションの関係はもう一捻りあります。
モバイルとそれ以外で異なるのは、主に画面サイズです(より正しくは前提とする通信環境によるリソースの出し分けなどもあります)。
一方、Webページとプレゼンにおいて、Webページはそれのみですが、プレゼンは(通常)「スライド+発表者+発表」で構成されます。この違いは「読者側が受動的か能動的か」「発表者による注意対象・情報量のコントロール」といった差異を生みます。

HTMLソースの話に戻ると、ある変換を行うとき、情報量は落とす方が楽で、足すのはかなり困難です。
つまり「より情報量を必要とする方を主とし、そうでないものを従とする」ということになります。
プレゼンは発表という追加情報を前提とするため、スライドにWebページと同じ情報量を与えると情報量が過多になりがちです。

CSSでは手軽に表示を隠蔽する方法としてdisplay:noneがあります。これは、プレゼンでは口頭で与える情報を、Webページではテキストで表示する、という目的にも使えます。

@media print {
.web-page-only {
  display:none;
  }
}

前提として、「スライド上隠蔽されても前後の話は分かるようにする」というライティングが必要です。なので、パラグラフライティングの基本や、DITAにおけるshortdescのように、「これは最低限最初に伝える」という文章から書いていくよいでしょう。

ページ向け媒体ではフロートはページの上端、下端を前提に書けます。これはpositionについても同様です。

XSLT超入門3の補足

さて、発表の補足です。

何度か述べましたが、XPathの特長は、簡単には「ある文書で、目的の箇所を簡便な記法で指定できる」「取り出した値の加工が行える」ことにあります。ただ「hoge/fuga/…」と書きつらねるだけでなく、軸や述部といった機能・概念を使うことで、他の言語、というかDOMインターフェイスでは難しいこともスマートに行える、ということをXSLT超入門3ではフューチャーしたのですが、いかがでしたでしょうか。
「ここが分からないのでここをじっくりやってほしい」などの要望を、メール・SNS・動画へのコメントなどでいただけると幸いです。

さて、発表について、事前の予定から大きく削った箇所としては2箇所です。データモデルと、XPath 1.0のコードを3.1でリライトするという内容です。

データモデルについては、XPath 2.0でいかに整理されたか、3.0(3.1)で何が拡充されたか、の詳細を削りました。このあたりについてはW3CのXDMのページに図示があるので内部処理モデルに興味のある方はご参照ください。

XPath 3.1でのリライトについては、題材として丁度良いバランスのものを見つけるのはかなり難しかったため、構成から削除しました。「XPath 1.0でまともに処理できるように記述した上で、XPath 3.1で明らかに改善されるように書き直す」ということの難しさは、「そもそも1.0で書けなかった処理を拡充したものがほとんど」「1.0では(XSLT上では)独自functionの定義もできないので、XSLTも多分に含むことになる」といった点にあると感じます。XSLT 1.0のコードでの超絶技巧についてはfunctXライブラリが有名でしょうか。

超絶技巧はおいておいて、もう少し簡単なものはあります。

XPath 1.0(XSLT 1.0)
<xsl:template match="*[contains(@class, ' topic/ph ')]"> 
  ...
</xsl:template>
XPath 3.0(3.1) (XSLT 3.0)
<xsl:template match="*[contains-token(@class, 'topic/ph')]"> 
  ...
</xsl:template>

行っているのはDITAの@class処理で、スペース区切りで続く文字列の部分一致判定です。contains()では含まれてさえいれば真を返してしまうので、topic/ph前後にスペースは必須です。ないと「mytopic/ph」なども一致してしまいます。
contains-token()は、なんだったら名指しで「HTMLやDITAのclass処理に使えるよ」と書かれているのでさもありなん。

コア関数についてはかなり駆け足で紹介しました。コード例をバーッと載せていましたが、コードをしっかり読んでもらうことは意図していません。
これは構成上割と悩みどころで、関数名と処理内容を淡々と流すのであれば、ウェビナーよりもWebサイトや本などを参照するように誘導しても良いと考えています。あるいは数時間~数日のワークショップであれば、サンプル、基本、応用、基本、……といった進め方をしたと思います。

とくにXPath 1.0では、関数型言語らしい「関数を組み合わせて使う」ことはなかなか難しいため、その有用性のアピールを分かりやすく行うのは難しくなっています。

発表中、contains()が(2.0)となっていましたがこれは記述ミスで、XPath 1.0からあります。


速習XSLT超入門1(明日に迫るXSLT超入門2ウェビナー )

ゴールデンウィークも明け、XSLT超入門2のウェビナーが明日に迫りました。

セミナーのようなイベントでは、ナンバリングによって「初回参加してないからどうしよう」と尻込みされてしまう方がいるかもしれません。ということで、第1回をおさらいする記事を用意しました。
「これだけ見ておけば大丈夫」というよりは、書籍におけるあらすじと目次のようなものと考えてください。

導入として「XSLTを活用する自動組版の流れとして、XSLTがどの部分の役割を果たすか」を紹介しています。元の文書にあるコンテンツから目次を生成したりできます。
このウェビナー内では大きく触れていませんが(PDF自動生成超入門の内容なので)、「生成時まで内容が決定できないこと」をオブジェクトとしてレイアウトできるのがXSL-FOとなります。弊社製品Antenna House XSL Formatterによる拡張要素・プロパティも、この視点で眺めてみるとスタイルシート設計に役立つのではないでしょうか。

書籍の完成状態を例にして、抽象的な「構造」について紹介し、それをXMLで表現することについて触れています。

XMLを変換するにあたって、「どの部分を変換するか」という指定が必要になります。XSLTでは、そのためにXPathを使うよ、ということを紹介しています。XPathはXSLTから独立するほど多様な機能がありますが、「XML上の特定位置を指定する」ことは基本といって良いでしょう。そのために「ノード」という形でXML文書を解釈し、ノード間の関係としてXML上の位置を指定できるようにしています。関係の方向性として「軸」があり、不足する指定を補う「述部」がある、という紹介をしています。

より実際的な説明として、弊社の過去記事を紹介しておきます。
XSLTを学ぶ (1) XMLのツリーモデルとXPath/XSLTのツリーモデルではルートの意味が違う

明日のウェビナーが「基本文法編」ということで、では「XSLTの基本」は何を紹介しているんだ、疑問があるでしょう。ここでは初学者にとって概念的にあまり馴染みがないであろう、XSLTを構築する基本であるxsl:template@matchとxsl:apply-templatesについてを図を用いて紹介しています。文法は比較的資料があるため、図示に注力した形です。後半のデモでトラブルがあった関係でウェビナーと動画で違いが生じてしまっていますが、このmatchとapplyの関係はコインソータに似ている、ということを覚えておくと良いでしょう。

ウェビナーではトラブルのあったデモについては動画を撮り直しています。
デモを通し、xsl:template@matchとxsl:apply-templatesでXML文書を処理していく様子を紹介しています。

全体を通して、「学習開始で環境構築に悩むよりプレイグラウンドなどを利用するのも良い」「業務利用としてXMLエディタは十分ペイする」「変換元のXML文書としてCommonMark文書が難易度として丁度良いのではないか」といった話をしており、「初学者が取り組みやすい形を提示する」をサブテーマとしていました。

明日のウェビナーではこの第1回からの流れを受けて、基本的なコードリーディングによる学習ができる段階まで持っていけるようにすることを目標にしています。ご参加をお待ちしています。


『Office Open XML Formats入門 第2版』を制作しました

2021年12月07日 16:00~17:00 に「ちょっと一息アンテナハウスウェビナー『Office Open XML Formats入門 第2版』制作報告」を発表しました。販売、公開よりも発表が先になってしまったため、内容が気になっていた方もいらっしゃるのではないでしょうか。

こちらが表紙画像です。

Amazon POD用表紙画像

2021年12月07日ウェビナーのバナー

組版をAH XSL Formatter V7.2で行ったため、『AH Formatter XML関連出版物の紹介』ページに掲載しています。

Amazonの販売ページへのリンクは次の通りです。

https://www.amazon.co.jp/gp/product/4900552836

また、本書のPDF版は弊社オンラインショップからご購入いただけます。印刷版とレイアウトの微調整を行いました。

https://web.antenna.co.jp/shop/html/products/detail.php?product_id=1301

HTML版はOffice Servers資料室のページからご覧いただけます。


ウェビナー概要や書籍紹介ページにある通り、アンテナハウス『Office Open XML Formats入門』の初版は2007年に出版社から刊行されました。
今年は2021年、つまり14年程前の書籍の改訂版ということになります。
内容的な修正は必要とはいえ、以前の版の原稿をベースに新たな版を制作するというとき、XML原稿はほとんど変更が必要ありません。以前の版の構造に不満がある場合はその限りではありませんが。

一方で大規模な修正の余地があるのがXSLTでした。そしてウェビナー(と書籍の後書き)では、XSLTについてはかなり省いて説明することになったため、本記事ともう一度どこかで補足することにしたいと思います。

2007年というのは、XSL的にもそこそこ大きな節目でした。XSLT 2.0のW3C勧告です。XSL 1.1の勧告は2006年でしたが、実利用として熟れていないという点では二者とも同様です。XSLT 2.0以降はミスの発見やスクリプトの見通しにおいてXSLT 1.0とは別言語に近い体験をもたらします。msxmlのXSLTが1.0であることや各ウェブブラウザほか処理系の多くが1.0までしか対応していないことも手伝って、使える状況が限られるのは悩ましいところです。

変更の概略

今回は自社事例でしたので、初版で1.0だったXSLTを3.0に書き換えました(完全に3.0向けに最適化したとはとても言えませんが)。

書籍初版の制作報告にもあったように、XSL 1.1での大きなポイントにbookmarkのとindex関係の語彙が入ったことが挙げられます。『Office Open XML Formats入門』初版では、bookmarkについてはXSL Formatterの拡張仕様、索引についてはbasic-linkとXSLTによる力技による解決が図られていました。

(AH )XSL Formatterの拡張仕様では実際のフローコンテンツ登場箇所と同一の箇所に記述するため、処理においてXML中の章構造に当たったときに同時に処理すれば良いことになります。よってXSLT記述としてはbookmark-tree用に処理を追加するよりも単純に書けます。XSLT 1.0で書く場合は複数回ドキュメントを走査することが難しいということも手伝っていたのかもしれません。

索引構造については、XSL 1.1のindexを導入することで、XSLTで行わなければならなかった処理が簡略化されます。索引語に当たったときの処理で、「既に同じ索引語が登場しているか」「同じ索引語が同ページに登場したときにページ数表示を合一する」といった判別がXSLのプロパティで変更可能になるため、分岐処理などが大幅に簡略化できました。

割とアドホックな書き換えを行っていたり、徹底できていなかったりするため、引き継ぎをするにはリファクタリングが必至ですが、大体このようなことを行いました。

  • テンプレート中に直接書き込まれたattributeをattribute-setへまとめる
  • 共通処理をまとめる。
  • apply-templatesを含まないような分岐は名前付きテンプレートへ追いだし、パラメータを渡すようにする
  • XPathによる値の取得はできるだけテンプレートの先頭でまとめ、利用箇所ではselect="$value"のように呼び出すだけにする

先に書いた通り徹底はできておらず、私自身も、テンプレート中で直接attributeを指定しているような箇所をかなり生み出してしまいました。とはいえ、ギリギリの時期に「ヘッダーの位置をもう少し下げて」といった指示に1行の変更だけで対応できるようにはできたので、無駄ではなかったと思います。

実は上に挙げたものはXSLT 1.0時点でも時間さえあれば行える変更で、3.0への変更には関係ありません。値のみを取得、操作したい箇所をfunction化したり、処理をreplace()関数に書き換えたりといった作業は2.0から行えます。3.0としては関数の括弧が入れ子ではなくarrowを使えたり、文字列結合に「||」を使えるといった枝葉の変更点を使用しています。

参考資料


XSL-FO 試行錯誤 カレンダーを自動生成したい(構想編)

師走まで間もなくとなり、日中の気温もかなり下がってきました。来年のカレンダーを用意する時期ですね。
ちょっと「XSL-FOでオリジナルのカレンダーを作りたい」なんてこともあるのではないでしょうか。

高々12月分であるのでDTPソフトウェアやXSL-FOの直書きで頑張っても何とかなるかもしれませんが、来年もある程度使い回せるようにしたり、2021年のように直前の変更があるかもしれないことを考えると自動化したいところです。

実現したいカレンダーは次のようなものを想定します。手書きであるため半端なところまでしか日付がありません。

実現したいカレンダーのラフ

今回は構想編ということで、方針や使っていくFO、XSLTのアタリを付けていきましょう。

ページレイアウト

カレンダーを作るにあたって、先ず決めなければいけないことは「どの程度グラフィカルにするか」、具体的にはページレイアウトをどこまで制限するか、ということです。
個別箇所の自由度を上げるとその分だけ自動化できる箇所は少なくなります。

とはいっても、「高々12月分」と書いたように12パターン程度であれば12通りのページレイアウトを用意しても良いでしょう。今回はページレイアウトを1つに定めることにします。

  • ページレイアウトは1つ
  • 毎月1ページで構成

(スケジュール帳を組むのであれば「1月分の中にページ分割はあるか」も考慮しなければなりません。AH XSL Formatterであれば見開き要素が使えるため比較的簡単に対応できます。その場合は、見開きの左右に分割される位置と格子の位置を上手く配置する工夫が必要になりますね。)

ページサイズはA3縦(縦420mm、横297mm)にしましょう。

毎ページ登場する内容

月ごとにページを分けるのであれば、各ページに共通して登場するのは
年でしょうか。来年であれば「2022年」ですね。これはstatic-contentに置くことにします。
また、年度(4月始まり)で設定したい場合、途中で年の表示は変わりますね。このことに備えて、マーカーで取り出した値を使うことにします。

後述するように画像を上半分にページ幅一杯に表示する際、画像の上にstatic-contentを被せて表示することが可能です。AH XSL Formatterではregion表示の優先順位も自由が利きます。

マス目表示を実現するFO

ここは奇をてらわずtableを使っていきます。

カレンダーでは、曜日をカラムのタイトルとして、1週目、2週目、…をrowのまとまりとして、それぞれの日付がセルとして表されます。
常にマス全体を長方形にすることとして、rowの数は5週分にします。

例示した画像のように日付の始まりと曜日の始まりが一致するなら並べるだけなので楽ですが、そうではないためXSLTで自動化することになります。当月の他、その先月または来月の数日間の情報が必要になることも、お手元のカレンダーから想像が付くのではないでしょうか。

元となるカレンダー用のXMLがある場合は、そこにテンプレートを適用するだけで済むでしょう。

日付の位置を調整して自動生成するXSLT

XSLT 2.0からは日付のための関数や型を利用できます。ある日付の曜日を取りたいときはたとえばformat-date('[w1]')とすればxs:integer型ではありますが曜日が取得できます。このことと日付用のduration、そしてmodを組み合わせればカレンダーのマス目が実現できそうです。

休日・祝日(今回は対応しない)

休日、祝日の背景色を変更することについて考えます。日曜を示すカラムが先に決まるため「毎週日曜の背景色を変更する」であればカラムに対して設定したりすれば良いでしょう。問題は祝日です。XSLTの標準ライブラリは日本の祝日対応まではしてくれていません。確実なソースとなると、内閣府のページにあるCSVでしょうか。

「国民の祝日」について – 内閣府

XSLT 2.0以降でCSVをパース可能なようにプログラムを書くこともできますが(<xsl:analyze-string>などを使います)、とりあえず今回は対応しないことにします。

そのほか二十四節気なども同様に、「どこかから日付と紐付いた外部ソースを得る」「XSLTで取り込んで展開する」といった手順になるでしょう。

各月のイラスト、写真の設定方法

話をFOメインに戻します。

ページレイアウトは1つだけにすることを決めました。
AH XSL Formatterではページマスターに背景画像を設定することも可能ですが、
1つのページレイアウトを使い回すのであれば、フローコンテンツとして指定していく形になるでしょう(実はマーカーに画像を指定することも可能ですが、テンプレートの記述量は大して変わりません)。

画像のクリッピングや位置調整については背景画像の方がプロパティ指定の余地が広いため、ブロックコンテナーの背景画像として配置することにします。

画像をページギリギリまで表示したい場合、フローコンテンツを塗り足し領域まで表示させる必要があります。この辺りのTipsは『AH XSL Formatter 拡張仕様使いこなしガイド』に載せていたりします。

次回予告

次回はカレンダーを実現するための関数、テンプレートについて考えていく予定です。

参考資料



SCSSで(S)CSS組版の見通しを良くする

Antenna House CSS FormatterはHTMLあるいはXMLとCSSからPDFを生成できます。

ところで、CSSを直接書くのは結構大変です。
スタイル設定を書いていると、数百行程度はすぐに超えてしまうかもしれません。
そうして膨れ上がったスタイル設定はメインテナンス性の観点からもあまり望ましくはありませんね。
そこで他の記述形式からCSSを生成する方法が普及してきました。
CSSを生成する言語あるいは処理系を指してメタCSSという呼称が用いられることもあります。メタCSSとしてはLESSやSCSSがあります。

本記事ではSCSSを記法として採用し、処理系としてsassを使用しました。SASSとSCSSの違いなどについては割愛します。SCSSは記法として素のCSS記法も可能なため、既に記述されているCSSからの移行が比較的容易いというメリットもあります。

今回はサンプルとして、弊社で公開している『CSSページ組版入門』のサンプルファイルを利用しました。
Antenna House CSS FormatterのバージョンはV7.2改訂1版を想定しています。

処理の流れとしては次のようになります。

1. SCSSファイルの記述
2. SCSSをCSSにコンパイル
3. HTML、XML+CSSをAH CSS Formatterで組版

sassの導入

sassは現在Dart言語で開発されていますが、インストールはDart環境よりもWeb系でより普及しているnpmやyarn経由の方が簡単かもしれません。

$ yarn add sass 

今回SCSSに変更していくbooklet-page-ja.cssをリネームし、booklet-page-ja.scssとします。SCSSに変更後、sassによって改めてbooklet-page-ja.cssを生成します。

$ yarn run sass booklet-page-ja.scss booklet-page-ja.css

ネスト記法

booklet-page-ja.css(変換前)

...
/* 目次 */
.TOC > ul {
  font-size: 15.5q;
  font-family: source-serif-pro, source-han-serif, serif;
}
.TOC li.TocLevel1 {
  font-size: 15.5q;
}
.TOC li.TocLevel2 {
  font-size: 14q;
}
.TOC li.TocLevel3 {
  font-size: 12.5q;
}

CSSでは基本的にセレクタは個別に書いていく必要がありますが、SCSSではセレクタの親子関係を入れ子で記述できます。また、「>」や同じ要素で別のクラス指定のものも入れ子にし、Parent Selector 「&」を使うことで目的の出力が得られます。
@pageはトップレベルのセレクタのため、「& :left」のような利用方法はエラーとなります。

booklet-page-ja.scss

...
/* 目次 */
.TOC {
   
 & > ul {
  font-size: 15.5q;
  font-family: source-serif-pro, source-han-serif, serif;
 }
 li {
  &.TocLevel1 {
    font-size: 15.5q;
  }
  &.TocLevel2 {
    font-size: 14q;
  }
  &.TocLevel3 {
    font-size: 12.5q;
  }
}
...

フォーマットした上での行数は増えることもありますが、同じ要素やクラスを親とするスタイル指定がすっきりまとまりました。

SCSSでの「&」は「&-top」のようにも使えるのですが、CSSのNesting Module(Draft)では処理が変わってしまうため、使用を控えています。

@mixin

SCSSでは@mixin <name> (<arg>) { ...}で指定した内容を@include <name>(<arg>)で取り出して使用できます。

フッタのページ番号とヘッダのテキストでfont-familyとfont-sizeが揃っていたため、@mixinを使用してみましょう。

booklet-page-ja.css

 ... {
  @top-left {    /* 柱(ページヘッダ)*/
    font-size: 11.5q;
    font-family: source-serif-pro, source-han-serif, serif;
  }
  @top-right { content: none }

  @bottom-left { /* ノンブル(ページ番号)*/
    font-size: 11.5q;
    font-family: source-serif-pro, source-han-serif, serif;
    content: element(Title);
  }
...
}
booklet-page-ja.scss

@mixin running-font {
      font-size: 11.5pt;
      font-family: source-serif-pro, source-han-serif, serif;
}
...
... {
    @top-left {    /* 柱(ページヘッダ)*/
      @include running-font();
    }
    @top-right { content: none }
  
    @bottom-left { /* ノンブル(ページ番号)*/
      @include running-font();
      content: element(Title);
    }
}

今回は使用していませんが、SCSSではCSSに変換時に置換される変数が使用できます。CSSにも変数機能がありますが、組版に使う処理系がCSSの変数に対応していないときなどはこちらが有用です。

AH CSS Formatter V7.2はCSS変数に対応しています。
AH Formatter V7.2 | CSS仕様の実装状況

@mixinなどと併用するときは粒度の管理が大切です。

ファイルをまとめる

素のCSSでは分割したファイルは都度HTMLのlink要素で読み込んでいたかもしれません。CSSの@import機能もありました。
「分割して細かくしていくうちに数十ファイルのCSSでどこに何が書かれているか分からなくなり、そのまますべて読み込むことにした」という話も耳にします。Webサイト読み込みにおけるパフォーマンス以外に、メインテナンス性にもかかわる話です。

SCSSでは複数の入力SCSSファイルから、1つまたは複数のCSSファイルを出力することもできます。
「SCSSファイルと同数のCSSファイルをチェックすることになり作業が倍に」といった事態にしないため、
出力するCSSファイルはまとめることにします。

複数の入力ファイルから出力を得る場合、sassへ指定する引数の書き方が少し変わります。
次のSCSSファイルのあるディレクトリをscss、出力するCSSファイルを含むディレクトリをcssとします。

$ yarn run sass scss:css 

scssディレクトリ内部のSCSSファイルがCSSへと変換されます。scss/a.scss、scss/b.scssファイルが存在する場合はcss/a.css、css/b.cssファイルが生成されます。

さてこのとき、ファイル名の先頭を「_」とすることで、そのファイルは出力するCSSファイルから除かれます。この除外されてしまうファイルの意義はどこにあるかというと、出力対象の別ファイルから内部的に読み込ませるためにあります。

出力するCSSファイルから除外するファイルを_color.scssとします。

booklet-page-ja.scss

@use "_color"
...

@useで読み込まれた_color.scssの内容は変換出力されたbooklet-page-ja.cssに反映されます。このとき名前空間などの概念が登場しますが、詳細はSCSS(SASS)のWebサイトなどをご覧ください。

ざっくりとした紹介となりましたが、メタCSSによってCSS組版の設定記述の見通しを良くする方法について説明しました。WebサイトでのSCSS-CSS利用との差異はほとんどありませんが、Webサイトよりも細かに異なるレイアウト指定を行うであろうCSSページ組版で、メタCSSによって得られる恩恵はより大きくなるのではないでしょうか。

参考資料



XSL-FO試行錯誤 原稿XMLで括弧類をマークアップで表現するメリット

何らかの変換によってXMLを得る場合や、スキーマにとくに定められていない場合、括弧類は通常の文字として記述されることが多いでしょう。

括弧を通常のテキストで記述する
である(ただし、Bの場合を除く)。

マークアップとして、括弧を表すタグを用意し、使用したとき、どのようなメリットがあるかについて考えてみました。

である<kakko>ただし、Bの場合を除く</kakko>。

<kakko>は多くの場合良いタグ名ではないでしょうが、目をつむることにします。

テキストとしての括弧ではなくタグを使ったとき、<kakko>をXSLTで処理するのは簡単です。

<xsl:template match="kakko"><xsl:text>「</xsl:text><xsl:apply-templates />」</xsl:text>
</xsl:template>

(テキストとしての括弧を検出したい場合、XSLT 2.0以降では正規表現で対応可能です。)

タグkakkoをどんなテキストで表すかの決定を、XSLTで変換するまで遅延できます。
また、XSL-FOで組版するとき、行頭、行末禁則の適用外のテキストでも疑似的に禁則があるように振る舞わせることができます。「\if」「\fi」を括弧の代替にしてみましょう。

<fo:inline keep-together.within-line="always"
keep-with-next.within-line="always"
>\if</fo:inline><fo:inline>ただし、Bの場合を除く</fo:inline><fo:inline 
keep-together.within-line="always"
keep-with-previous.within-line="always"
>/fi</fo:inline>
keep-*による分割防止

それぞれ直前、直後の文字で分割できそうな場合でも、くっついて処理されていますね。
@keep-*については次のページをご覧ください。
第6章 プロパティ値の書き方 『XSL-FOの基礎 第2版 – XMLを組版するためのレイアウト仕様』

デメリットとしては、禁則制御そのものではないため別の分割規則との都合で目的と異なる挙動とるかもしれないこと、原稿から抽出したテキストを機械翻訳に渡すときにうまく調整する作業が発生するかもしれないことなどが挙げられます。


XSL-FO試行錯誤 スライドPDFをXSL-FOで組む

以前に、スライドをXSL-FOで作成した例をご紹介しました。
今回、明日9/17(金)17時よりオンライン開催される「FormatterClub2021」用のスライドを改めてXSL-FOで作成したのでご紹介します。

まずは明日の宣伝です。

FormatterClub2021ウェビナー

【どなたでも無料視聴可能】

日時
2021年9月17日(金)17:00~18:10
概要
今回のFormatterClubでは、キヤノンの吉田一様にFormatterでのマニュアル作成と自動組版の取り組みを発表していただく他、XSL拡張仕様のご説明、AH Formatterの今後をご紹介を致します。
内容紹介・お申込みページ
ウェビナー登録ページ

今回はPowerPointで下書きを作成した後、清書をXMLで行い、XSLTでXSL-FOに変換するという方法を取っています。以前のウェビナーではPowerpoint原稿をそのまま使ったのですが、見出しや各箇所の文字サイズといった、同じ内容の繰り返しが多い事柄に対し個別に変更を行っていくのはちょっと辟易してしまったためです。
Powerpoint文書もXMLの集合ではあるのですが、PresentationMLはとくにDrawingML色が強く、いってみればグラフィカルな配置に重点が置かれているため、「流し込んだら良い感じに自動配置してほしい」といった要求のための元テキストを得るのは恐らくWord以上に困難です。

XSLTのバージョンは3.0で動かしています。記事中に登場するFO、XSLTの動作について保証するものではありません。ご了承ください。

XML

次のようなXMLからXSL-FOを生成します。

<section xml:id="about"><title>概要</title>
   <div style="slide" layout="cover" />
 ...
   <div style="slide" layout="fig-desc">
   <description>
     <dl>
       <dlentry>
         <dt>書名</dt>
         <dd>AH XSL Formatter拡張仕様使いこなしガイド</dd>
       </dlentry>
       <dlentry>
         <dt>公開日</dt>
         <dd>2021年5月18日</dd>
       </dlentry>
       <dlentry>
         <dt>概要</dt>
         <dd>XSLをAH XSL Formatterが独自に拡張した仕様についてのガイド
           <list style="unordered">
            <li>複合的なショウケース</li>
            <li>「やりたいこと」から引ける逆引きリファレンス</li>
          </list>
        </dd>
      </dlentry>
     </dl>
   </description>
   <figure>
      <image href="./XSL-FO-Extensions.png"/>
   </figure>
 </div>
 <div style="slide" layout="plain">
 ...
 </div>
</section>
...

若干の制限を設けたブロックとしてdescriptionタグを用意していますが、概ねHTML5程度の表現力のXMLを用意しました。汎用タグdivで何でも表そうとすると、子や親の要素・属性に制限を設けることが難しいという欠点にぶつかります。

dlentryはdtとddの組を区切るのに使用しています。HTML5のようにdivでないのは上と同じ理由です。区切りを前提にすることによってXSLT上でのパースがかなり楽に行えます。

ulとol相当はlistのclassとして使い分けます。CommonMarkのDTDなどはこのように扱っていますね。

テーマ擬き

Powerpoint、あるいは多くのプレゼンテーション用ソフトには「テーマ」という、スタイル設定の集合があります。
Word文書などと同様、Powerpoint文書はレイアウト用と見た目用の構造が渾然一体となっているのでテーマ切り替えだけでは大抵万事上手くは行かず適宜調整することになります。とまれ、見た目上の違いが印象に大きな影響を与えるスライド文書では類似の機構があると便利です。

今回は次のレイアウトを用意しました。

  • 「表紙用レイアウト」
  • 「大項目扉ページレイアウト」
  • 「通常ページレイアウト」
  • 「段組レイアウト」
  • 「画像とその説明用レイアウト」
  • 「大きめの画像表示レイアウト」

大きく異なるレイアウトについては異なるページマスターとして用意します。
通常ページ、段組、画像とその説明、大きめの画像表示レイアウトは同一のページマスターです。また、スライド全体の表紙と、大項目扉ページは同一のページマスターです。両者の違いは、fo:region-startfo:region-beforefo:region-afterの領域の有無、またそれを考慮したfo:region-bodyであるかです。

表紙系のページ

表紙以外のページ

(AH XSL Formatter拡張仕様ではページシーケンスの入れ子が行えるので「同じ項目内容の途中でスライドのレイアウトを切り換える」という要求を簡単に満たすことができます。この辺りも元のXML構造の設計に影響します。)

表紙系でないページでは、小項目のタイトルをfo:region-beforeに表示することにします。スライドではヘッダーとしての表示と見出しとしての表示を兼用してもあまり違和感がありませんね。また、大項目をfo:region-startに配置します。16:9のスライドの場合、画面の左端から右端までが4:3に比べ遠くなるため、タイトル領域として活用することにしました。こうすることで、大項目と小項目を両方表示したいとき、1つのヘッダーに大項目と小項目を両方表示するよりも、あまり文字を小さくせずにすみます。

段組レイアウトはAH XSL Formatter拡張仕様であるブロックコンテナへのcolumn-count指定で実装しました。

画像とその説明用のレイアウトはインラインコンテナを並べて実装しています。インラインコンテナは分離しないよう、念のためkeep-with-*などを指定します。幅指定を行わないと2つめのインラインコンテナが次の行へ送られてしまったり、高さ指定を合わせておかないとline-stacking-strategyによっては配置がガタガタしてしまいますから注意が必要です。

画像とその説明用のレイアウト
<fo:block>
 <xsl:attribute name="line-stacking-strategy">line-height</xsl:attribute> 
   <fo:inline-container width="48%"
     height="100%"
     keep-with-next.within-line="always">
     <fo:block>
        <xsl:apply-templates select="child::description"/>
     </fo:block>
   </fo:inline-container>
   <fo:inline-container width="50%" height="100%">
     <fo:block>
       <xsl:apply-templates select="child::figure"/>
     </fo:block>
   </fo:inline-container>
 </fo:block>

ほかの方法としては、「region-bodyを複数用意してflow-nameを切り換える」、「画像を左右へのフロートで配置する」といったものが考えられます。フロート配置は王道ですが、スライド用途の場合、AH XSL Formatter拡張でなければ思ったように(目的のスライド位置に)配置することはかなり難しいかもしれません。

箇条書き

スライドでは箇条書きが頻出する傾向があります。端的に内容を示し、口頭でのプレゼンに詳細内容を譲るためなど理由は考えられますが「様々な場所で登場する」ということに留意する必要があります。

XSL-FOのリスト構造はstart-indentとend-indentが肝要です。

ブロックのインデントは基本的には親の値を引き継いでいれば問題ありませんが、start-indentを指定したブロックの入れ子にリストブロックがあるケースなどでは、うっかり領域の端からの値にならないよう、inherited-property-value(*-indent)で親のインデントの値を取得してから自分のインデントを追加する必要があります。

<!-- 入れ子レベル$listLevelは計算済 -->
...
<xsl:attribute name="provisional-distance-between-starts">
 <xsl:value-of select="'inherited-property-value(start-indent) + 0.5em + ' || $listLevel || 'em'"/>
 </xsl:attribute>

fo:list-item-labelの内部はブロックなので、ラベルに使える記号の自由度は高いです。今回は入れ子レベルによるラベルの切り換えは行いませんでした。

定義箇条もリストブロックにしました。同じDL内のDTの文字列長を比較し、最長のものを基準にインデントを決定しています。あまりにDTが長ければ単純なブロックに切り換えます。今回は和欧混合文などは考慮しません。


<xsl:template match="dl">
 <xsl:variable name="dt-lens">
   <xsl:for-each select="child::dlentry/dt">
     <len>
        <xsl:sequence select="xs:double(string-length(./text()))"/>
     </len>
   </xsl:for-each>
 </xsl:variable>
 <xsl:variable name="max-length-dt" select="xs:integer(max($dt-lens/len/text()))"
   as="xs:integer"/>
 <xsl:choose>
   <xsl:when test="$max-length-dt &lt; 20">
     <fo:list-block
       provisional-distance-between-starts="inherited-property-value(start-indent) + {$max-length-dt}em + 2em"
       provisional-label-separation="1em">
        <xsl:for-each select="child::dlentry">
           <fo:list-item>
             <fo:list-item-label ...">
                <fo:block ...>
                   <xsl:apply-templates select=".//dt"/>
                </fo:block>
             </fo:list-item-label>
             <fo:list-item-body ...>
                <fo:block ...>
                   <xsl:apply-templates select=".//dd"/>
                </fo:block>
             </fo:list-item-body>
           </fo:list-item>
         </xsl:for-each>
     </fo:list-block>
   </xsl:when>
   <xsl:otherwise>
    ...
   </xsl:otherwise>
  </xsl:choose>
</xsl:template>

最長である「公開日」の長さを基準にインデントが決定されています。均等割り付けの指定は「:」を含んでしまっているので「概要」の後のスペースなどはちょっと微妙です。end方向に揃えるか、axf:text-autospaceで揃えるか、ブロックの横にフロートかブロックコンテナで「:」配置すればより良くなるでしょう。

枠内にテキストを収める

大項目の扉ページでは、見出しテキストを四角い枠内に収めることにしました。

スライドは本と比べ、ページごとの独立性が高くなることが多いでしょう。「1枚の中に必要なコンテンツを収めたい」というとき、フォントサイズや字間、伸縮などを最適なものから外しても1枚のスライド内に収めるため、オーバーフロー処理を記述します。

どうにかテキストを枠内に収めるにしても限界値はあります。より汎用的に使うのであれば、文字数制約や、枠自体のサイズを指定し直すような記述を追加することになるでしょう。


今回採用、実装しなかったものとしては「自由配置の吹き出し」や「画像の上から付ける下線」などがあります。グラフィカルな配置はスライドとしても重要な要素ですが、代用が可能なものでもあります。たとえば画像側で下処理を行っておけば、清書としては都度処理する必要はありません。
とはいえ、まったくそれらの処理ができないのも不便ではあります。
今後、このようにXMLからPDFスライドを作成する機会があれば、SVGやブロックコンテナをページ上の座標など簡単なパラメータで任意の場所に配置するような形態で実装するかもしれません。

関連記事



Pages: 1 2 3 4 5 6 7 8 9 10 11 12 Next