Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PDFMaker: pre-defined tcolorbox support #1637

Merged
merged 12 commits into from
Feb 8, 2021
Merged

PDFMaker: pre-defined tcolorbox support #1637

merged 12 commits into from
Feb 8, 2021

Conversation

kmuto
Copy link
Owner

@kmuto kmuto commented Jan 6, 2021

デフォルトの囲みがイマイチというのを言われ続けていますが、
MITライセンスの https://github.com/Yasunari/ascolorbox にサンプルが揃っていたのでこれを利用できるようにするとどうかなと思い、機能を作ってみました。

こんな感じで設定します。ascolorboxの環境によってオプションが異なるので、パラメータは各環境およびそのボックス処理メソッド依存です。

pdfmaker:
  boxsetting:
    note:
      style: simplesquarebox
      thickness: 0.8
    important:
      style: ascolorbox4
    memo:
      style: ascolorbox9
    info:
      style: ascolorbox16
    warning:
      style: ascolorbox19
    caution:
      style: ascolorbox1
      options: 'colupper=white,colback=red!70!black,fontupper=\sffamily\gtfamily'
    notice:
      style: ascolorbox3
    column:
      style: ascolorbox13
      options: 'fonttitle=\large\sffamily\gtfamily\bfseries,fontupper=\sffamily\gtfamily'

review-ext.rbでLaTeXBoxクラスにbox_スタイル名(config, name) (configは設定構造体、nameはcolumnなど)、返り値は[スタイルファイル名, 環境開始文字列, 環境終了文字列 ] (終了文字列は省略可)を追加すれば、それを呼び出せるようにしています。
ascolorboxのほうはLuaLaTeXに対応していないのがちょっと残念なところです(数箇所直すだけで動きはするので直してもらおうかな…)。

方針としてよさそうならあとはテストなど追加しますね。

@kmuto kmuto changed the title PDFMaker: pre-defined colorbox support PDFMaker: pre-defined tcolorbox support Jan 6, 2021
@takahashim
Copy link
Collaborator

うーーん、ascolorboxを使うという方針自体は良いんではないかと思いますが、実装面でbox_*メソッドをsendする&review-ext.rbを使わないといけないのがちょっとメタすぎる気がしました。
実際のbox_*メソッドの定義は結局のところthickness等のデフォルト値を与えることくらいしかやっていないようなので、これならそれこそYAMLなりLaTeXなりで上書きできるくらいになっていればいいんでは…と思います。
(YAMLで与える場合、以下のような書き方を想定してます)

pdfmaker:
  boxsetting:
    note:
      style: simplesquarebox
      options: ['0.8']
    important:
      style: ascolorbox4
      options: ['2']
    memo:
      style: ascolorbox9
      options: ['3']
    info:
      style: ascolorbox16
    warning:
      style: ascolorbox19
      options: ['2']
    caution:
      style: ascolorbox1
      options: ['colupper=white,colback=red!70!black,fontupper=\sffamily\gtfamily']
    notice:
      style: ascolorbox3
    column:
      style: ascolorbox13
      options: ['fonttitle=\large\sffamily\gtfamily\bfseries,fontupper=\sffamily\gtfamily']

@kmuto
Copy link
Owner Author

kmuto commented Jan 8, 2021

ascolorboxだと単純なのでbegin/endの1行で済むんですが、汎用の呼び出し方法としてはかなり変えられるようにしないといけなさそうで(手元で使っているようなものはもっといろいろ処理がある)、またRe:VIEWコアとのインタフェースとしては現状 review-ext.rb / layout.tex.erb / config-local.erb / styでTeXを記述 のどれかになり、この中だと扱いやすいのはreview-ext.rb くらいしかなさそうなんですよね。
boxのためだけの新たな決めうちの何かを作るのもためらわれる(決めうちにしてconfig.erbから呼び出すということはできそうですが)。

optionsですが、optionsはascolorboxのどれも取れます(実際にはtcolorboxのオプションとして単なる取り込むだけ)。それ以外のthicknessなどのパラメータが環境によってあったりなかったりという感じです。
なので結局擬似コード的にはこんなかんじで最初のと変わらない(単に階層がさらに増えてる)ものになってしまいそうです。

note:
      style: simplesquarebox
      options: { thickness: '0.8', otheroption: 'colupper=white,colback=red!70!black,fontupper=\sffamily\gtfamily' }

@takahashim
Copy link
Collaborator

具体的に複雑な例を見せてもらえると対処法がわかるかもしれません。

私の方で想定していたのは↓こんな感じのコードで(動作は未検証、configはコンストラクタの引数にしています)、

require 'review/logger'
module ReVIEW
  class LaTeXBox
    def initialize(config)
      @logger = ReVIEW.logger
      @config = config
    end

    def tcbox
      ret = ''
      stys = Set.new

      %w[column note memo tip info warning important caution notice].each do |name|
        style = setting.dig(name, 'style')
        next unless style

        sty, beginenv, endenv = environment(name, style)
        stys.push(sty)

        endenv ||= %Q(\\end{#{setting[name]['style']}})
        ret << <<EOT
\\renewenvironment{review#{name}}[1][]{%
 #{beginenv}}{%
 #{endenv}}
EOT
      end

      stys.map! do |sty|
        if sty =~ /\[/ # with sty option
          %Q(\\usepackage#{sty})
        else
          %Q(\\usepackage{#{sty}})
        end
      end

      stys.join("\n") + "\n" + ret
    end

    def environment(name, style)
      options = setting[name]['options'] || []
      opt_args = options.map{|opt| "[#{opt}]" } || "[]"
      [
        'ascolorbox',
        %Q(\\begin{#{style}}{##1}#{opt_args})
      ]
    end

    private

    attr_reader :config

    def setting
      config['pdfmaker']['boxsetting']
    end
  end
end

kmutoさんの挙げた例だと↓こんなふうに書けるはずです。

note:
      style: simplesquarebox
      options: ['0.8', 'colupper=white,colback=red!70!black,fontupper=\sffamily\gtfamily' ]

結局 options はLaTeX知らないと書けないので、LaTeXベタ書きでもいいのでは…という気持ちです。
そういう意味では options: '[0.8][colupper=white,colback=red!70!black,fontupper=\sffamily\gtfamily]' みたいな書き方でもいいのかもとも思いました。

@takahashim
Copy link
Collaborator

あるいは路線を変えて、Re:VIEW用のenvironmentをそれぞれ作った方が早かったりするでしょうか…。

@kmuto
Copy link
Owner Author

kmuto commented Jan 9, 2021

ありがとうございます、巧みな感じはありますが、この場合、独自にstyと環境名を用意して追加したい場合はenvironmentメソッドはどう拡張することになるのでしょう? (sty名もymlで指定させればいいのかな。)

あと、中期目標としてCSSっぽく設定できるtcolorboxラップを作れば、あまりTeXに触らずに実現できるのではというのを考えていたのでした。この場合だと引数では無理で、かつ全部TeX側に押し付けても実装困難なので、ある程度Re:VIEW内のRubyコード側で整理・変換をしておきたいです。

note:
  style: sugoibox
  border-width: 1mm
  border-color: cmyk(0, 5, 0, 0)
  background-color: cmyk(0, 100, 0, 0)
  padding: 3mm

@munepi
Copy link
Contributor

munepi commented Jan 9, 2021

(難しいな―と思いながら、このissueを眺めていたのですが、)
tcolorboxによる環境ごとに、引数のとり方を自由にできてしまうので、

  • options をどの順番に取るのか、
  • はたまた「見出し」を取るのか(見出しがないなら、ない場合の挙動も与えるのかどうか)、

などなど、画一的に決めることが難しいなーと思っていました。

実際に、ascolorboxで定義している環境たちも、引数のとり方がマチマチで、唯一、追加で与えるオプション options の引数は、最後の引数となるように定義されていますね。

なので、kmutoさんでいうところの boxsettingからpre-definedされるtcolorboxによる環境の定義は、引数のとり方に一定の制限が必要そうでしょうか。
それ以外のブロックがほしい場合は、従来どおり、review-ext.rb で用意していただくと。

@takahashim
Copy link
Collaborator

@kmuto ascolorbox 以外も使うのであれば、それもYAMLで指定できるようにする(省略時はascolorboxにする)、でしょうか。
例えばminicolumnで定義されている名前以外のcaptionblock等を導入するのであれば(つまりRe:VIEWの構文を追加する場合)review-ext.rbを変更する方法しかなさそうですが、スタイルの変更であればreview-ext.rbを使わないに越したことはなさそうです。
「CSSっぽく設定できるtcolorboxラップ」はまだ実装が想像できてないですが、極力Re:VIEW本体側で定義したいところかと。

@munepi 「引数のとり方を制限する」or 「引数をまるごと与えられるようにする(ほぼLaTeXの構文を文字列として与える)」になるんではないかなあ、と思っております。

@kmuto
Copy link
Owner Author

kmuto commented Jan 10, 2021

ascolorboxはタイトルなしだといろいろ悲惨になったので、デフォルトにするのはよくなさそうでした。
まずはもうちょっと汎用的に扱いやすいtcboxを作るところからやっぱりやらないとだめですね…。

@kmuto
Copy link
Owner Author

kmuto commented Jan 17, 2021

ascolorboxはdead upstreamっぽいので、forkして改造することにしました。

https://github.com/kmuto/ascolorbox

とりあえずキャプションなしの場合の見た目が変にならないようにするのと、オプションはバラバラにするのではなくオプション引数に混ぜたいと思っています(が、それはできるのかな…)。

@kmuto
Copy link
Owner Author

kmuto commented Jan 23, 2021

キャプションあり・なしを1つの環境で作るのは、内部で2つに分けるにしてもかなり複雑になってしまいそうということがわかってきました。munepiさんの懸念のとおりですね…。

  • \review装飾名@caption{キャプション}{固有xparse引数}{tcolorbox汎用xparse引数}\review装飾名@nocaption{固有xparse引数}{tcolorbox汎用xparse引数} みたいにキャプション有無で2つの環境を基本形として定義しておくものとする
  • \reviewnote\reviewnote[caption]かを \review装飾名〜のどちらかに分岐… というのがTeX/LaTeXコーディングレベルでできるか。単純なrenewenvironmentレベルで済まなくなりそうな。
  • 固有xparse引数、tcolorbox汎用xparse引数 はどうやって渡すか…

@takahashim
Copy link
Collaborator

キャプションの有無等で、Ruby側から生成するコードを分けるのは普通にできるんではないかという気はします(ちょっと冗長になるかもですが)。

@kmuto
Copy link
Owner Author

kmuto commented Jan 23, 2021

分けるのはできるけど、今展開されているRe:VIEWプロジェクト環境と互換性を持つのをどうしたものかなぁという悩みがあるのでした。tcbox拡張モードを使うならそっち、と分けちゃえばいいのかな。

@kmuto
Copy link
Owner Author

kmuto commented Jan 23, 2021

etoolboxを使って分岐はできました。xparseは2つ持たせても解析タイミングがないのでダメっぽい。

\renewenvironment{reviewnote}[1][]{%
  \csdef{review@withcaption}{true}
  \notblank{#1}{
    \begin{rvsimplesquarebox@caption}{#1}[colback=black!50!white]
  }{
    \csundef{review@withcaption}
    \begin{rvsimplesquarebox@nocaption}[colback=black!50!white]
  }
}{
  \ifcsdef{review@withcaption}{
    \end{rvsimplesquarebox@caption}
  }{
    \end{rvsimplesquarebox@nocaption}
  }
}

@kmuto
Copy link
Owner Author

kmuto commented Jan 23, 2021

etoolboxで分岐、tcbsetで取り込み時パラメータを増やせることがわかったので、わりと目処がついてきました。
あとはYAMLなのですが、カスタム設定などでやはりキャプション有無で分けないと後が困ることもわかりました。

pdfmaker:
  boxsetting:
    note:
      - style: simplesquarebox
      - withcaption-options: "rv line width=0.8, title={NOTE: #1}"
      - nocaption-options: "rv line width=0.8"
    important:
…

オプション名はwithcaption/nocaptionよりもうちょっといい名前がないかな…。
あとはそのまま渡す都合で、パラメータ値間違えると容易に変なエラーが生まれることになりますが、それはしょうがないですね。

@takahashim
Copy link
Collaborator

pdfmaker:
  boxsetting:
    note:
      style: simplesquarebox
      options:
        with_caption: "rv line width=0.8, title={NOTE: #1}"
        no_caption: "rv line width=0.8"
    important:

とかでしょうか。

@kmuto
Copy link
Owner Author

kmuto commented Jan 24, 2021

うぅ、6段になっちゃいますね…。

pdfmaker:
  boxsetting:
    note: ["simplesquarebox", "rv line width=0.8", "rv line width=0.8, title={NOTE: #1}"]
    囲み名: ["環境名", "キャプションなしの引数・キャプションありがなければ省略可", "キャプションありの引数・省略可。省略した場合はキャプションなしの引数を適用"]

と指定順で表す、というのはどうでしょう…。今後これで済まなくなったときに困るかな。

@takahashim
Copy link
Collaborator

note:とかの右に来る要素が長くなっても、順番の意味が分かりづらい&拡張しづらい&特に書きやすくもならないような気もするので、縦に並べてもいいんではないかと思いました。

ネストを深くしないのであればこんな感じでしょうか。

pdfmaker:
  boxsetting:
    note:
      style: "simplesquarebox"
      options: "rv line width=0.8"
      options_with_caption: "rv line width=0.8, title={NOTE: #1}"
    important:
      ....

@kmuto
Copy link
Owner Author

kmuto commented Jan 24, 2021

なるほど、よさそうです。
しかしascolorboxはキャプションなし版を作るのがえらく大変なのと、オプションの差し込み方がad-hocすぎて、もっと汎用的に作り直さないと厳しいなということに…。

@kmuto
Copy link
Owner Author

kmuto commented Jan 28, 2021

ascolorboxはあきらめ、自前のスタイルを作りました。upLaTeX, LuaLaTeXでテスト済みです。

もう少しパターンを増やせるとよいのですが、ページ分けやキャプションの対応がだいぶ大変だったので、インターフェイスとしてこうすることにしたというのと、リファレンス実装スタイルということにしたいと思います。

%キャプションありスタイル定義
\DeclreTColorBox{rv@スタイル名@caption{ m O{} }{
 tcolorbox定義,
title={#1},
#2}

%キャプションなしスタイル定義
\DeclreTColorBox{rv@スタイル名@nocaption{ O{} }{
 tcolorbox定義,
#1}

tcolorbox定義にない独自のオプションを作りたいときにはtcbsetで定義することになります。
実際のところ、スタイルの追加はユーザーが個々にやるというより、TeXに詳しい人のcontributionを待つ感じですね。

キャプション有無でどちらのマクロ定義を使うかの判断はlatexbox.rbで設定しています。

YAMLのほうは @takahashim さん提案のとおり以下の記法としました。オプションを間違ったり、styleで定義のないものを入れると変なエラーにはなりますが、そこまで面倒見るのは無理そうです。

pdfmaker:
  boxsetting:
     note:
       style: スタイル名
       options: キャプションなし版オプション
       options_with_caption: キャプションあり版オプション(省略した場合はoptionsと同じ)

ということで、これでひとまず入れようかと思いますがいかがでしょう。

あと変えるとすれば review-tcbox.sty が同一なのをコピーするのが微妙というところですが、これをいじるとreview-init・review-updateあたりがだいぶ複雑になってしまうのでこのままでもよいかな…と日和りました。

@munepi
Copy link
Contributor

munepi commented Jan 28, 2021

@kmuto ものすごく簡単な例ですが、

\DeclareTColorBox{rvnote}{ O{} }{
  empty,
  before skip=\baselineskip,
  after skip=\baselineskip,
  boxsep=3mm, boxrule=0mm,
  top=0mm, bottom=0mm, left=0mm, right=0mm,
  breakable,
  % frame
  borderline={0.1mm}{0mm}{black}, arc=0mm,
  % title
  coltitle=black, fonttitle={\bfseries},
  before upper={\noindent\tcbtitle\par},
  detach title,
  % otherwise
  #1}

としておいて、

\begin{rvnote}[title={コラム見出し}]
これはノート見出しありのノート環境です。
\end{rvnote}

\begin{rvnote}[]
これはノート見出しなしのノート環境です。
\end{rvnote}

\begin{rvnote}%%<= 引数のparseの都合上、常に [] があったほうがよいと思う。ノート本文の最初が「[」で始まるかもしれないので。
これはノート見出しなしのノート環境です。
\end{rvnote}

とするのはどうでしょうか?
Re:VIEWのコンパイラから、noteのタイトルを吐くのであれば、タイトル自体を引数にtitle={タイトル}としてしまってもよいかもです。

(タイトル出さないオプション notitle を入れてしまう手もあるといえばありますね。ただ…、attach titleして、見出しありとなしで大幅に見た目が違ったり、調整が必要なタイトル見出しの場合は、言わずもがな…、別々のデザインを割り振ってしまったほうがよいわけですが……。)

@kmuto
Copy link
Owner Author

kmuto commented Jan 29, 2021

ありがとうございます、確かに先頭[問題があるのでそのケアをしておいたほうがいいですね。
単純にnocaptionのほうは「取るけど何も使わず無視」にするのがいいかな…

今は使ってないですが、ascolorboxを見るとtitle前提で空にしたときにひどい結果やエラーになることがままあり、tcolorbox記法内で処理分岐することもできないので、有無の環境は分けたほうがよいなと判断しました。

@kmuto
Copy link
Owner Author

kmuto commented Feb 8, 2021

だいたい必要な対応はできたと思うので、入れます。(ドキュメントを作りたいけど、tcolorboxマニュアルになってしまいそう…)

@kmuto kmuto merged commit fbb2d9c into master Feb 8, 2021
@kmuto kmuto deleted the ascolorbox branch February 8, 2021 01:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants