Skip to content

Feature: spot-color() and color-tint() CSS functions for Separation color space support #1836

@munepi

Description

@munepi

Related: #430 (support color model), vivliostyle/vivliostyle-cli#774 (CMYK conversion audit)


Is your feature request related to a problem? Please describe.

商業印刷では、blend color、metaric ink、CMYK色域外の色などに、(単色)特色(DIC, PANTONE 等の特色)が必要です。
現在の Vivliostyle は device-cmyk() による /DeviceCMYK 出力に対応していますが、以下の2つの問題があります。

  1. 特色(Separation 色空間)に対応していない。 device-cmyk() は PDF の /DeviceCMYK のみを出力でき、/Separation(特色版)を出力できません。

  2. CSS に tint(濃淡)を指定する手段がない。 LaTeX の xcolor パッケージでは DIC161s!50(50% tint)や black!20(黒 20%)のように簡単に書けますが、CSS には相当する機能がありません。ユーザーが device-cmyk(0 0.32 0.5 0) のように手動で CMYK 値を計算する必要があり、ミスの温床になります。

In commercial printing, spot colors (DIC, PANTONE, etc.) are required for brand colors, metallic inks, and out-of-gamut colors. Vivliostyle currently supports /DeviceCMYK via device-cmyk(), but cannot output PDF /Separation color spaces. Also, CSS lacks a tint mechanism equivalent to xcolor's ! operator.


Describe the solution you'd like

2つの CSS 関数の追加を提案したいです。

1. spot-color(NAME): 特色参照

特色を名前で参照する関数です。
名前は vivliostyle.config.js で定義します。

h1 { color: spot-color(DIC161s); }

2. color-tint(COLOR, N%): 汎用 tint 関数

任意の色に tint(濃淡)を適用する汎用関数です。
これはLaTeXにおける xcolor パッケージの ! 演算子に相当します。
spot-color 専用ではなく、rgb(), device-cmyk(), 名前付きカラーなど、すべての色モデルで動作します。

/* 特色の tint(xcolor の DIC161s!50 に相当) */
.tint50  { color: color-tint(spot-color(DIC161s), 50%); }

/* プロセスカラーの tint(xcolor の black!20 に相当) */
.black20 { color: color-tint(black, 20%); }
.cyan50  { color: color-tint(device-cmyk(1 0 0 0), 50%); }

/* RGB の tint */
.red90   { color: color-tint(rgb(255 0 0), 90%); }

/* tint と alpha(透明度)の併用: これらは独立した概念です */
.both    { color: color-tint(device-cmyk(0 0 0 1 / 0.5), 50%); }

tint 演算は「白との混合」として定義されます(xcolor の ! と同じ)。

色モデル color-tint(color, N%) の意味
CMYK (0,0,0,0) 各成分 × N/100
RGB (255,255,255) c + (255−c) × (1 − N/100)
Spot Separation tint 値 = N/100

特色の設定

特色は vivliostyle.config.js一箇所でのみ定義 します。
CSS と config の両方に CMYK 値を書く二重定義の問題を避けるためです。

// vivliostyle.config.js
export default {
  output: [{
    path: 'output.pdf',
    pdfPostprocess: {
      cmyk: true,
      spotColors: [
        { name: 'DIC161s', pdfName: 'DIC 161s*', cmyk: { c: 0, m: 0.64, y: 1, k: 0 } },
        { name: 'DIC256s', pdfName: 'DIC 256s*', cmyk: { c: 0.9, m: 0, y: 0.4, k: 0 } },
      ],
    },
  }],
};

spotxcolor(LaTeX)との対応関係

本提案は、LaTeXにおける拙作の spotxcolor パッケージの「Intercept, Detect, Replace」戦略に基づいています。

spotxcolor                           Vivliostyle (proposed)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
\definespotcolor{DIC161s}          → vivliostyle.config.js spotColors[]
  {DIC 161s*}{0, 0.64, 1, 0}
\color{DIC161s}                    → spot-color(DIC161s)
DIC161s!50                         → color-tint(spot-color(DIC161s), 50%)
black!20                           → color-tint(black, 20%)
\set@color intercept               → CLI post-process intercept
|c_i - t·b_i| < ε → Separation    → same algorithm → Separation

期待される PDF 出力

%% Separation 色空間オブジェクト
<< /FunctionType 2 /Domain [0 1] /C0 [0 0 0 0] /C1 [0 0.64 1 0] /N 1 >>
[/Separation /DIC#20161s* /DeviceCMYK <func ref>]

%% Content stream
/DIC161s cs 1 sc         % spot-color(DIC161s) — 100% tint
/DIC161s CS 1 SC         % stroking
/DIC161s cs 0.5 sc       % color-tint(spot-color(DIC161s), 50%)
0 0 0 0.2 k              % color-tint(black, 20%) — DeviceCMYK fallback

重要: tint と alpha(透明度)は異なる概念です

印刷においてこの2つは、根本的に異なる操作です。
しばしば印刷現場で起きていることは、一部のWebデザイナーが opacity で「薄い色」を作ろうとするのは、印刷では誤りです。
color-tint() が印刷で正しい代替手段を提供します。

tint alpha(transparency)
意味 インキ濃度(網点面積率) 合成透明度
PDF オペレータ 0 0 0 0.5 k 0 0 0 1 k + ExtGState /ca 0.5
版分解 1版で50%ハーフトーン フラット化(透明の解除)が必要
併用 color-tint(device-cmyk(... / 0.5), 50%) = tint 50% かつ alpha 0.5

Describe alternatives you've considered

A. CSS 変更なし(config + proportional tint 検出のみ)

CSS 側は device-cmyk() をそのまま使い、CLI の config で特色名と CMYK 値のマッピングを定義する方式。
CLI の後処理で proportional tint 検出(spotxcolor の |c_i - t·b_i| < ε アルゴリズム)により、CMYK 値が特色の比例 tint であることを自動検出する。

不採用の理由

  • ユーザーが device-cmyk(0 0.32 0.5 0) のように tint 値を手計算する必要がある(ミスの原因)
  • CSS と config の両方に特色の CMYK 値を書く二重定義になる
  • CSS 上で特色の意図が読み取れない

B. CSS custom property のみ

:root { --DIC161s: device-cmyk(0 0.64 1 0); }
h1 { color: var(--DIC161s); }

不採用の理由

  • tint の問題は解決されない(var(--DIC161s) に 50% tint を適用する CSS 構文がない)
  • config との二重定義の問題が残る

C. color-mix() の実装

CSS Color Level 5 の color-mix() を実装する方式。

不採用の理由

  • color-mix() は汎用的すぎて、tint(白との混合)だけでなく任意の2色混合を扱う
  • Separation 色空間の tint 値に直接対応しない
  • 実装が大きくなりすぎる

Additional context

実装方針

vivliostyle.js(Core)

  • spot-color()color-tint() を CSS 関数としてパース(Vivliostyle 独自 CSS 拡張の前例あり: crop-offset, env(pub-title) など)
  • Webブラウザ上のプレビュー用に color(srgb ...) に変換(device-cmyk() と同じ代替 RGB 方式)
  • getSpotColorMap() API で特色マップをエクスポート(getCmykMap() と同パターン)
  • 既存の device-cmyk() パイプラインへの変更なし

vivliostyle-cli

  • spotColors 設定スキーマの追加
  • proportional tint 検出アルゴリズム(spotxcolor より移植: |c_i - t·b_i| < ε
  • pdf-lib を用いた PDF Separation オブジェクト生成
  • content stream 書き換え: 特色にマッチ
    • rg/RG/NAME cs TINT sc、不一致 → k/K(DeviceCMYK fallback)

既知の制約(設計上の判断)

ここで相談したいこと

  1. 関数名: color-tint() vs tint() vs 他の良い案はありますか?
  2. Vivliostyle 独自の CSS 関数を追加することへの懸念はありますか?

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions