技术文章

在 Delphi 里把 PDF 转换为同一种色彩空间

RecolorDocument 把整份 PDF 移入同一种色彩空间,RecolorPage 则对单页做同样的事。两者都接收 1 表示灰度、2 表示 RGB、3 表示 CMYK,并返回转换了多少种颜色。几何形状、文本和图像的位置分毫不动,改变的只有颜色

这正是把作品交给一台只接受单一空间的印刷机之前想要的那一步操作,也是把彩色文档转成灰度文档却无法重新生成时想要的那一步。另一种做法——用源数据在目标空间里重建文档——只有在你仍持有源数据时才可行,而对任何从外部收到的文件你都没有源数据

哪些会被转换,顺序又是怎样

首先是颜色算子:页面在绘制时设置的填充和描边。已经在目标空间里的颜色原样保留,所以一页颜色无需改动的页面会保留原始字节,而不是被改写成完全相同的值。这个性质在大文档上比听上去更重要,因为未被触及的对象在文件里就保持未被触及

接下来是图像,而这正是过去缺失的部分。一份只转换了算子就变成灰度的文档,仍会在灰度页面上显示彩色图片,这比两种极端都更糟。一张图像满足以下条件时会被转换:每个分量 8 位、设备色彩空间、并且要么无压缩要么仅用普通 Flate

索引图像通过它的颜色表转换:调色板被转换,而每个样本保持它已有的索引。这覆盖了截图或图表通常保存为的调色板图像,无论样本是用什么方式打包或压缩的都能转换,因为样本本身从不改变

var
  Lib: TPDFlib;
  Converted: Integer;
begin
  Lib := TPDFlib.Create;
  try
    Lib.LoadFromFile('brochure.pdf', '');
    Converted := Lib.RecolorDocument(1);       // 1 grey, 2 RGB, 3 CMYK
    Log(Format('%d colours converted', [Converted]));
    Lib.SaveToFile('brochure-grey.pdf');
  finally
    Lib.Free;
  end;
end;

为什么 JPEG 原样保留

因为转换它的颜色意味着重新编码,而重新编码是另一种操作,后果也不同。JPEG 或传真图像保留自己的字节:解码、转换、再压缩会改变图像数据本身,引入第二代有损瑕疵,并以调用方未曾要求的方式改变文件大小

软遮罩和模板遮罩被保留是出于另一种原因。两者都作为覆盖率(coverage)而非颜色来读取,转换其中之一会让页面把遮罩当作图片来显示——本该塑造内容的透明遮罩,被渲染成叠在内容之上的一层灰度画作。保留它们不是要绕过的限制,而是正确的结果

通过 ICCBased、Separation、Indexed 或 Pattern 空间设置的颜色,在算子层也按原样保留。不读取它们的 profile 就去转换,会改变页面外观而非保留外观,而一项承诺就在于保留的重着色流程不能去猜测 profile

渐变连同它背后的色标一起转换

一个渐变由一个色彩空间加上一个在该空间里产生颜色的函数组成,只转换其一不转换其二就会产生无意义的结果——一个色标被落下的已转换渐变,会被读成某个通道被误认作灰度级别,在页面上看起来就是一条颜色完全错误的色带。渐变的色彩空间和它背后的色标一起移动

无论渐变存在于何处都能被找到:作为独立对象、写在绘制它的图案(pattern)里、或位于页面的着色资源(shading resources)中。这种覆盖面之所以重要,是因为设计工具存放渐变的方式各不相同,而一个只处理一种放置方式的转换器会让一份文档转换到一半,失败之间还看不出明显规律

有两类渐变刻意保持原样。由采样函数或 PostScript 函数构建的渐变保留自己的定义,网状(mesh)类型也是如此——它们把颜色放在流里而不是函数里。改写这两者与转换颜色是不同的操作,在重着色流程里悄悄做掉会令人意外

// Convert a single page - useful when only the cover needs to change
Lib.SelectPage(1);
Lib.RecolorPage(3);          // this page to CMYK
Lib.SaveToFile('cover-cmyk.pdf');

重着色在送印前的合适位置

把它当成归一化步骤,而不是色彩管理。重着色在设备空间之间转换数值;它不应用 ICC profile、不遵从渲染意图(rendering intent)、也不模拟印刷机条件。对于必须达到合同样张(contract proof)要求的活件,基于 profile 的工作流才是答案,重着色至多是先把所有内容归入同一空间的那一步

它真正发挥价值的,是日常生产问题:一份必须灰度却带 RGB 标志的来稿、一份不能彩色打印的内部文档、一批在预检步骤接受之前必须统一到一种空间的文件。给它配上一道检查——PDF/A 与 PDF/UA 预检合规与签名工作台的笔记覆盖了如何把判定结论作为同一项作业的一部分跑起来

如果文档还需要的是检视矢量图稿而非转换,矢量图形、着色器与渐变的详解覆盖了这些对象最初是如何构建的,这是理解一次重着色能触及什么、不能触及什么的有用背景

PDFlibPas 是面向 Delphi、C++Builder 和 Lazarus 的原生 Pascal PDF 库,没有外部运行时,所以色彩归一化步骤在你自己的服务里运行,而不是 shell 出去调用一个转换器——完整的文档处理 API 见 PDFlibPas 产品页