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

XSL-FO試行錯誤 索引の索引関連FO以外の箇所

個人的な話として、索引周りは既にあるなら自前で実装したくはない箇所です。

索引に関連した仕様はXSL-FO 1.1で追加されました*1。1.1は2007年には登場しており、私が学び始めた時点で既に存在します。

先日それ以前に書かれたコードに触れる機会があり、<fo:basic-link>とXSLT 1.0で書かれた処理を見て苦労を偲びました。

索引関連のFOはアンカーと、アンカーの参照、その参照をまとめて書式などを調整するものがあります。

組版するまで未決定なページ番号関連の挙動を制御してくれる索引のFOですが、逆に、索引対象語のソートなどはXSLTなどで、索引の表示レイアウトは通常のブロックやリーダなどで行うことになります。

テキストの揃えやリーダ関係ではAH Formatterのマニュアルにあるように*2指定すれば、よく目にする「テキストはstartの端、ページ参照はendの端」という見た目の索引とできるでしょう。目次もこのようなレイアウトを取ることがあります。

索引の索引語ごとのブロックとしては@text-align@text-align-last、(axf:text-align-first)で揃えを制御し、@keep-together.within-pageでページ参照部分だけ次ページに分割されてしまうことがないように、という指定を行うことになるでしょうか。このときインラインの構造としては「索引語」「リーダ」「索引参照のFO」という並びになります。

さらに大きい構造として、「索引の見出し文字」と「索引語ごとのブロックの連続」となります。このとき見出し文字のブロックに@keep-next.within-pageでこの箇所での分割を避けておく必要がありますね。

参考資料



XSL-FO試行錯誤 SVGでもっとグラフィカルなボーダーを

Antenna House Formatterではボーダーに指定可能な線種について大きく拡張されています。

しかし、それでもボーダーの意匠について自由自在とまでは行きません。
例えば「ロープで結ったような枠線を出したい」というときには、背景画像を置くことになるでしょう。ボーダーの扱いで(他の多くと同様に)難しいところは、ページ分割のときにどうするかです。
背景画像に固定枠の画像を指定したとき、段落の長さによって枠をずらすようなことはできませんね*。ページ分割時にbeforeとafter部分のボーダーを再表示する必要がなければ、段落の前後に画像を挟めば良いだけで、テーブルレイアウトにする必要はありません。

さて、XSL-FO試行錯誤シリーズ、以前の回でテーブルレイアウトを駆使してブロックがページ分割されるごとに特定の要素を出現させる方法を紹介しました。
つまり、テーブルレイアウトのヘッダとフッタにボーダーとなる画像を指定し、テーブルのボディのbeforeとafterの位置に表示させてやろうというのが今回の試みです。

やることは単純明快。ソースコードは単純とはいきませんが。
早速結果を見てみましょう。

可変長の段落に対応した装飾的なボーダー

今回のソースコードは(特に)色々無駄があります。見ながら一緒に突っこんでいただければ幸いです。

ソースコード
<fo:block-container>
  <fo:table table-omit-header-at-break="false" table-omit-footer-at-break="false">
    <fo:table-header>
      <fo:table-row>
        <fo:table-cell>
          <fo:block-container 
            absolute-position="absolute"
            top="-2cm" left="-2.6cm"
            width="100%" height="6cm"
            axf:background-content-height="scale-down-to-fit"
            background-image="url(./corner.svg)"
            axf:background-size="cover">
          </fo:block-container>
        </fo:table-cell>
      </fo:table-row>
    </fo:table-header>
    <fo:table-footer >
      <fo:table-row>
        <fo:table-cell>
          <fo:block-container 
            width="100%" height="10cm">
            <fo:block-container
              absolute-position="absolute"
              top="-4cm" left="3cm"
              width="100%" height="10cm"
              axf:background-content-height="scale-down-to-fit"
              axf:transform="rotate(180)"
              background-image="url(./corner.svg)"
              axf:background-size="cover">
           </fo:block-container>
         </fo:block-container>
       </fo:table-cell>
     </fo:table-row>
   </fo:table-footer>
   <fo:table-body>
     <fo:table-row>
       <fo:table-cell>
         <fo:block space-before="4cm"
           space-before.conditionality="keep" start-indent="2cm" end-indent="2cm">
 Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

...
 
         </fo:block> 
       </fo:table-cell>
     </fo:table-row>
   </fo:table-body>
  </fo:table>
</fo:block-container>

思いつきのままに書いたのでもっとやりようがある気もします……。

corner.svgがボーダーとして用いる画像です。上のコードのように、わざわざaxf:transformで回転するよりも、もう1つフッタ位置用の画像を用意した方が早いでしょう。

ボーダー分、段落のbeforeにスペースを空けています。ボーダー画像を背景に指定しているブロックコンテナにabsolute-positionを設定しているためで、ブロックとfo:external-graphicでボーダー画像を配置した場合は不要です。SVGのサイズをきちんと把握して作成しておけば、もっとスッキリした指定になるでしょう。
background-sizeにcoverを指定していますが、coverである必要もないですね。左上の画像は高さの指定と合わず切れているので怒られかねません。

今回はbeforeとafterのボーダーに注目しました。ではstart、endのボーダーはどうすればいいのでしょうか? これは実はより単純で、背景に指定したボーダー用の画像をbackground-repeatでブロック進行方向に繰り返すことで、ある程度可変長の段落に対応できるようになります。ただ、ボーダー画像の端で上手く繰り返せるように段落の高さが背景画像の高さの整数倍になるようにする、beforeとafterのボーダー画像と破綻しないようにする、といった調整が必要になるでしょう。
beforeとafterについては背景画像ではなくfo:external-graphicやfo:internal-graphicでもあまり問題はないのですが、startとend方向は繰り返しの指定上背景画像への指定が良いのではないでしょうか。

* 後から検討したのですが、背景画像を指定した複数のブロックコンテナを入れ子で記述して描画位置を調整すればある程度可能そうです。結局ページ分割の問題にあたってしまいますが。

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まですべてやる」というのはかなり体力が必要です。自動組版を検討するにあたって「文書作成から組版までのどの過程を自分たちで行い、どの過程を自分たちではやらないようにするか」を判断する一材料としてもお役に立てるかもしれません。

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



XSL-FO試行錯誤 最初からハイフンのある語の制御

5月18日に『AH XSL Formatter拡張仕様使いこなしガイド』が公開・発売したことは
当日の記事で述べました。

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

『XSL-FO 試行錯誤』連載ではこれまで密かに「AH XSL Formatterの拡張仕様はメインの対象にしない」という縛りでやってきたのですが、めでたく書籍が公開されましたので、これからは拡張仕様についても対象にしていきます。『AH XSL Formatter 拡張仕様使いこなしガイド』に載せられなかった話題について扱うかもしれません。たとえばAH XSL FormatterではCSS3にあるような装飾表現が可能であったりするのですが「CSS3を紹介したWebサイトを見れば良いのでは」となってボツにしたものがそこそこあります。

制作のXSL-FO以外の面についての話についても折を見て記事などで展開する予定です。気になった点などがあればコメントやSNSなどで言及していただければ反映できるかもしれません。

最初からハイフンのある語の制御

ということで、本記事の話題は見出しの通り。どういったものがあるかというと、コマンドラインのオプション引数(-optionとかですね)、XSL-FOの要素、プロパティ名など(border-top-styleなど)ですね。

そもそもこれらは行分割位置に来ないようにするのがおそらく理想的ですが、ページ数の制約などで妥協することもあるかもしれません。

もとからハイフンがある語は分割によるハイフンを追加しない

<fo:block ...
 hyphenate="true"
 axf:hyphenate-hyphenated-words="false">...
</fo:block>

サンプルFOのページのサンプルでもよく使われています。プロパティ名など、もともとハイフンを含む語にハイフンが追加されてしまうと、元からあったハイフンなのか、分割時に追加されたハイフンなのか分からなくなってしまうかもしれませんからね。

他の回避手段としては、ハイフネーションとして追加される記号を変更するという方法も考えられます。

ハイフンを含む特定の文字列を、分割可能でないハイフンに置き換える

(XSL-FOの標準仕様として、「行分割でハイフンが追加され得る単語で、分割前後の文字数によって分割位置を調整する」ことができるhyphenation-remain-character-counthyphenation-push-character-countがあります。)

さて、元からハイフン(-)を含む語があり、初期設定でこのハイフンの後では分割可能であるとします。しかし、この文字の後が1字のみのとき(「UTF-8」など)のとき、「8」だけ次の行にいくのはあまり望ましくないですね。

「分割しないハイフン(NON BREAKING HYPHEN)」(U+2011)という文字があります。この文字でハイフンを置き換えてあげれば、ハイフンの前後で行分割は通常起こりません。
では、すべてのハイフンを置換しますか? それはそれで文字数の調整が狂ってしまいかねません。
また、元のファイルが500ファイル以上になっていたので、時間的に個別に置換するのはちょっと困難だとします。

今回は「特定の文字の組み合わせ(-*)でのみ、1字の次行送りが発生する」ことが分かっていたので、axf:text-replaceを使うことにしました。

axf:text-replace / CSS (-ah-)text-replace AH Formatter マニュアル


<xsl:attribute-set name="code">
<xsl:attribute name="axf:text-replace" select="'-&#x2217;' '&#x2011;&#x2217'"/>
</xsl:attribute-set>

指定に確認していない漏れがなければ上手く行っているはず。


この成果(書籍全体で適用されているか)は『AH XSL Formatter 拡張仕様使いこなしガイド』で確認してください!
(XMLファイルを処理するときにXSLTは経由したわけで、XSLT 2.0の正規表現などによる置換なども可能ですが、その辺りをあまり変更したくない、変更できない場合、ありますよね?)


『AH XSL Formatter 拡張仕様使いこなしガイド』



『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 評価版のお申し込み



XSL-FO試行錯誤 索引でページ番号をmergeできるのは3ページから

XSL-FOの索引でページ番号が連続するとき、最初と最後のページ番号だけ表示して途中のページを省略するプロパティがあります。

  • 索引キーが連続するページに登場する
  • 索引キーのindex-classが同じである(index-classが異なるとマージ処理が指定されていても分離される)
  • merge-ranges-across-index-key-referencesプロパティ”merge”が設定されている

このときmerge-sequential-page-numbersmergeを指定いると、ページ番号表示が範囲形式にまとめられます。範囲形式のときに最初のページ番号と最後のページ番号の間の文字は<fo:index-page-citation-range-separator>で指定可能です。

仕様にも「3ページ以上のとき」とあり、2ページのときはとくに何もしません。

この機能は、索引に指定した語の登場ページを列挙するときに表示をまとめる機能であり、「トピックと語がほぼ一致するので、索引表示はトピックの範囲が分かるようにしたい」という要望のためのものでは「ない」ということです。
具体的には、筆者は「2ページ連続のときも範囲形式で表示したい」とFOを記述しようとして「そういう機能ではない」という指摘を受けました。

ちなみに「トピックの範囲を常に範囲形式で表示したい」というときはどうするかについては、「トピックは必ず2ページ以上となる」という制約を付けられるのであれば(見開き2ページを基本構成とするなど)、<fo:page-number-citation>を使えば良いでしょう。制約がないと、トピックが1ページの範囲のとき「3ページ-3ページ」のような表示になってしまうかもしれません。(<fo:static-content>内のマーカ参照でのこういった重複については、AH XSL Formatterではaxf:suppress-duplicate-marker-contentsという拡張仕様があります。)


索引 22–2–2 連続ページ番号の範囲表示 | XSL-FOの基礎 第2版

索引自体の記述例はサンプルFOをご覧ください。
https://www.antenna.co.jp/AHF/ahf_samples/sample-fo.html#page-set


XSL-FO試行錯誤 リストアイテムのインデントが効いていない?

改まった内容の記事は結構時間がかかりますが、このブログ連載のタイトルが「試行錯誤」なので、凡ミスを載せたりしても良いかな、ということで。
実際に起こった失敗より、一部改変しています。

XSLTで箇条書きにする変換を書いていたときのこと。

FOに変換後、PDF出力すると一部のリストブロックだけ箇条書きのラベルの上に箇条書きの内容が。
これ自体はインデントの指定を間違ったときにすぐ起こるので、あまり悩むことはありません。どこかでアイテムラベルかアイテムボディのインデント指定を上書きしてしまったのでしょう。

しかし、アイテムラベルやアイテムボディの周辺を見回しても特に不味いところは見当りません。仕様を把握している方は「ああ、それは多分……」とこの時点で見当がついたかもしれません。

それでは次のようなコードで、どんなことが起こり得るでしょうか?

<xsl:attribute-set name="atsList">
  <xsl:attribute name="provisional-distance-between-starts">ホニャララ</xsl:attribute>
  <xsl:attribute name="provisional-distance-label-separation">フガフガ</xsl:attribute>
</xsl:attribute-set>
<xsl:attribute-set name="atsListLabel">
  <xsl:attribute name="start-indent">いいかんじ</xsl:attribute>
  <xsl:attribute name="end-indent">label-end()</xsl:attribute>
</xsl:attribute-set>

<xsl:attribute-set name="atsListBody">
  <xsl:attribute name="start-indent">body-start()</xsl:attribute>
  <xsl:attribute name="end-indent">ほどほど</xsl:attribute>
</xsl:attribute-set>


<xsl:template ...>
  <fo:list-block xsl:use-attribute-sets="atsList">
    <fo:list-item>
      <fo:list-item-label
       xsl:use-attribute-sets="atsListLabel">
        <fo:block xsl:use-attribute-sets="atsListLabelBlock">
          LABEL
        </fo:block>
      </fo:list-item-label>
      <fo:list-item-body xsl:use-attribute-sets="atsListBody">
        <xsl:apply-templates select="ナンチャラ"/>
      </fo:list-item-body>
    </fo:list-item>
  </fo:list-block>
</xsl:template>

答え合わせにいきましょう。

<!-- 変換後のFO -->
      <fo:list-item-body start-indent="body-start()" ... > 
        <fo:block start-indent="0pt" >
        ...
        </fo:block>
      </fo:list-item-body> 

実際に問題の指定があったのはこの箇所ではなく<xsl:apply-templates>の先、アイテムボディの内容を記述するブロックでした。start-indent="0pt"の指定があるブロックへ変換していた箇所があったのです。XSLTでattribute-setを分けていたため、気づくのが遅れました。それでも変換結果のFOを確認しながら作業していればもっと早くに気づけたのではないかと思います。

設計するときは、小さなテストケースや検証環境をあらかじめ用意しましょう。ホントに。


XSL-FO試行錯誤 XSL-FOにおけるテーブルレイアウトの利点

かつて多くのWebサイトでは、table要素を表組ではなくレイアウトのために用いる「テーブルレイアウト」が多く見られました。
これはテーブルをグリッドのように使うもので、rowspanやcolspanを組み合わせ、比較的簡単に(そして恐ろしく複雑な)Webページを作成できました。
衰退していった理由は非推奨であること*1やCSSで求めるレイアウトが可能になったこと、アクセシビリティへの関心の高まりなどさまざまにありそうですが、その中には「テーブル構造では異なる画面サイズに最適化した表示調整が困難」という理由もあるのではなかろうかと個人的には考えています。

つまり、出力時一時的に変換される、固定レイアウトを前提としたXSL-FOではいまだそれなりに有用ということです。
とはいえ、それこそHTMLにおけるテーブルレイアウトのようにグリッドの代用として使うのは最終手段にしたいところで、XSL-FOにおけるテーブルレイアウトの利点は他にあります。

static-contentとretrieve

さて、<fo:retrieve-marker>という名前のFOがあります。これを活用しstatic-contentでflow中に記述されたマーカから要素を引っぱることが可能です。static-contentはヘッダやフッタに用いられます。

任意のマーカからflowの任意の場所で要素を引っぱって来れれば可能な表現も増えるのですが、それはできません*2

flow中でヘッダ・フッタを持てるFO

ところで、flow中でヘッダ・フッタを持てるFOがあります。はい、<fo:table>です。しかもページ分割のときヘッダフッタの表示是非も指定可能なのです*3。ちなみにAH XSL Formatterでは段分割でomitするかどうかを制御可能です*4
そしてこの<fo:table-header><fo:table-footer>中では<fo:retrieve-table-marker>によってマーカから要素を引っ張れます。

そして、<fo:table-cell>にはブロックレベルのFOが格納可能です。この意味するところがおわかりになりますでしょうか。

あるセルの中のブロックがページ分割されるとき、当然(例外もあるかもしれませんが)セルもページ分割されます。ヘッダやフッタにretrieveのFOを置くことにより、マーカで中身を更新できますから、次のような表現だって可能になります。

分割後に「……continued」をヘッダ部に表示

テーブル唯一のセル中にブロックコンテナを置き、ブロックコンテナが分割されるときに「……continued」と表示されるようになりました。

<fo:table>以外のブロックでも分割前後に表示を変更可能なFOが仕様としてあることが個人的には望ましいですが、(XSL-FOにおける)テーブルレイアウトは、簡易的なflow内static-contentとして有用なのです。

ちなみに、「ヘッダやフッタではbeforeやafterにしか表示できずブロックのstartやend側の端に何かを表示できないのでは」という懸念は、限定的に解消可能です。ブロックコンテナには絶対配置というものがありましたね。(ただし、絶対配置のブロックコンテナは、他のオブジェクトと重なり得る点に注意が必要です。)

  1. *1

    Tables must not be used as layout aids. Historically, some web authors have misused tables in HTML as a way to control their page layout. This usage is non-conforming, because tools attempting to extract tabular data from such documents would obtain very confusing results. In particular, users of accessibility tools like screen readers are likely to find it very difficult to navigate pages with tables used for layout.

    https://html.spec.whatwg.org/multipage/tables.html#the-table-element
  2. *2 基本的にflow内でページ分割が影響しない事柄はXSLTが担う役目であるのでFO文書の不要な複雑化を避けられる制約でもあるのですが。
  3. *3 17–4 表のヘッダーとフッター 『XSL-FOの基礎 第2版』(アンテナハウス)
  4. *4 https://www.antenna.co.jp/AHF/help/ja/ahf-ext.html#axf.table-omit-header-at-break, https://www.antenna.co.jp/AHF/help/ja/ahf-ext.html#axf.table-omit-footer-at-break



XSL-FO試行錯誤 fo:flow-mapの注意事項

前回、flow-mapの概要と、簡単な例、若干極端な利用方法と失敗例を紹介しました。

今回も注意深く使わなければ失敗してしまうという例を紹介します。

2つのregionであるregionA、regionBへ1つのflowを流し込むようなケースです。(というより、ほかの2つはこのパターンよりも複雑なので、利用しようとしたとき「うっかり失敗する」というケースは少ないのではないでしょうか。)

脚注はそれぞれのregionごとに配置される

一見1つのregion-bodyに見えますが、実は上下2つのregion-bodyで構成され、flow-mapでxsl-region-bodyを流し込んでいます。前回見たようにregionのサイズを超えるオブジェクトを挿入すると表示が壊れるかもしれませんし、あまりメリットがないように思えます。このflow-mapがどんなときに有用かというのは今後紹介するかもしれません。

とりあえず上手くいったように見えるFOですが、大きな欠点があります。

脚注をページ下部に配置するとき、正確にはregion-bodyのbottomから脚注の分の領域が確保されます。flow-mapはあくまでマッピングを行うだけで、region-bodyが1つになったわけではありません。つまり次のようなことが起こり得ます。

脚注をflow-mapで1つに見せかけたregionに流し込む
 <fo:flow flow-name="xsl-region-body">
   <fo:block >Lorem ipsum dolor sit amet<fo:footnote>
       <fo:inline ...>*<axf:footnote-number id="a"/></fo:inline>
         <fo:footnote-body>
           <fo:block ...>Lorem ipsum ...</fo:block>
         </fo:footnote-body>
     </fo:footnote>, consectetur ...
  </fo:block>
</fo:flow>

上部のregionAの下部に脚注は配置されました。たとえば左右に分けられているregionではそれぞれのregionの下部に表示してほしいでしょうし、当然の挙動ではあります。

複雑な回避方法を考えるよりも、このページシーケンスとflow-mapを使うときに「脚注を使用しない」「図表を使用しない」と制限した方が事故を防げるでしょう。
AH XSL Formatter拡張仕様ではregion-startやregion-endを注の配置に利用可能なので、「脚注ではなく傍注を利用する」といった方法も考えられます。

https://www.antenna.co.jp/AHF/help/ja/ahf-ext.html#axf.footnote-position

flow-mapで幅の違う疑似段組も実現可能だが、段抜きはできない

AH XSL Formatterではregion-bodyのほか、ブロックコンテナに段組を指定可能ですが、段組は通常段の行進行方向の長さが均等になります。
https://www.antenna.co.jp/AHF/help/ja/ahf-fo11.html#fo.block-container
flow-mapを活用することで、段の幅が異なる段組を擬似的に実現可能なのですが、span="all"を指定しても段抜きができないなど、これもまた使い所を考える必要があります。

幅の異なる(疑似)段組

これらのFOでは、表組などでもうっかり破綻する可能性があるので、くれぐれも注意深く使用しましょう。

参考資料


前々回:XSL-FO試行錯誤 脚注のインデント
前回:XSL-FO試行錯誤 fo:flow-map 概要編


XSL-FO試行錯誤 fo:flow-map 概要編

今回はflow-mapについてです。今回の記事は「概要編」としてflow-mapとはどんなものなのか紹介します。

region-bodyは複数記述可能

<fo:region-body><fo:simple-page-master>に複数記述可能です。region-body同士は重なり得るので、適切に配置します。<fo:flow>@flow-nameで利用するregion-bodyを指定します。

region-bodyを複数使う例

<fo:layout-master-set>
  <fo:simple-page-master master-name="main"
     size="JIS-B5 portrait" margin="10mm">
     <fo:region-body region-name="en" margin-right="182mm div 2 - 5mm"/>
     <fo:region-body region-name="jp" margin-left="182mm div 2 - 5mm"/>
   </fo:simple-page-master>
 </fo:layout-master-set>
 <fo:page-sequence master-reference="main">
   <fo:flow xml:lang="ja" flow-name="jp" >
     <fo:block ...>日本語</fo:block>
     ...
   </fo:flow>
   <fo:flow xml:lang="en" flow-name="en">
     <fo:block ...>English</fo:block>
     ...
   </fo:flow>
</fo:page-sequence>

region-bodyを複数使う例

たとえば、翻訳文を併記したいけれどテキストの長さが元の文と異なってしまうときなどにも対応可能ですね。表組や段組で左右の文の位置が揃うよう並べるよりも構造がすっきりしているのではないでしょうか。表組の解除や段抜きなどはないため、図版だけページ中央に表示したいなどの要求があるとややこしくなりますが。

flow-mapについて

本記事の本題はflow-mapです。名前の通りflowのマッピングを行える機能です。簡易な理解ではページシーケンスマスタでのページシーケンスとページマスタの関係を、flowとregionに置き換えたものとなります。ページシーケンスマスタのように条件よる参照分岐はできません。

XSL-FOの基礎 | 第4章 ページレイアウトの切り替え

JIS X 4179『拡張可能なスタイルシート言語(XSL) 1.1』で紹介されているflow-mapの使い方は3パターンほどです。簡単な例としては次のような形があります。

  1. 別々のregionであるregionA、regionBに対し、同じflowを流し込む
  2. 1つのregionに対し、別々のflowであるflow1、flow2を流し込む
  3. regionA、regionBにflow1、flow2を流し込む

この中でもっとも使い方として分かりやすいのは「regionA、regionBに対し同じflowを流し込む」という形でしょう。

flow-mapで2つのregionを1つのflowに対応させる
<fo:layout-master-set>
  <fo:simple-page-master master-name="complex"
     size="JIS-B5 portrait" margin="10mm">
     <fo:region-body region-name="regionA"
       margin-top="1cm" margin-bottom="130mm"
       margin-right="182mm div 2 - 5mm"/>
     <fo:region-body region-name="regionB"
       margin-top="180mm" 
       margin-left="182mm div 2 - 5mm"/>
   </fo:simple-page-master>
  ...
  <fo:flow-map flow-map-name="flowmap">
   <fo:flow-assignment>
     <fo:flow-source-list>
       <fo:flow-name-specifier flow-name-reference="flow-merged"/>
     </fo:flow-source-list>
   <fo:flow-target-list>
     <fo:region-name-specifier
       region-name-reference="regionA" />
     <fo:region-name-specifier 
       region-name-reference="regionB"/>
   </fo:flow-target-list>
   </fo:flow-assignment>
 </fo:flow-map>
<fo:layout-master-set>
<fo:page-sequence master-reference="complex"
  flow-map-reference="flowmap">
  <fo:flow flow-name="flow-merged"/>
    <fo:block>...
    <fo:block>
  &t;/fo:flow>
</fo:page-sequence>

flow-mapで2つのregionを1つのflowに対応させる

<fo:flow-map>@flow-map-nameを指定し、<fo:page-sequence>でそのflow-mapを参照するようにします。
流し込まれるソースのflowの@flow-nameと同じ値を<fo:flow-source-list>の子、 <fo:flow-name-specifier>@flow-name-referenceに、流し込み先のregionを<fo:flow-target-list>の子、<fo:region-name-specifier@region-name-referenceに指定します。今回はregion2つに対してflow1つを割り当てました。

<fo:page-sequence>@flow-map-referenceで参照するflow-mapを指定する必要があります。
複数flow-mapを用意すれば、同じflow-nameのflowに対し、flow-mapの参照を変更することで別のregionに流し込むことも可能です。

さらに細かいregionとflow-mapを利用することで、次のようなレイアウトも可能です*

沢山のregionをflow-mapで割り当てる

試行錯誤であるところはここからで、このregionはマッピングされているとはいえそれぞれ独立です。たとえばフォントサイズを大きくするとテキストの位置がずれ、次の行のテキストと重なるかもしれません。

一部のフォントサイズを変更した結果テキストが重なった