color.rs 文件是一个用于PDF处理的颜色空间管理模块,主要实现了PDF标准中定义的各种颜色空间。内容如下:
rust
use datasize::DataSize;
use crate as pdf;
use crate::object::*;
use crate::error::*;
#[derive(Object, Debug, DataSize, DeepClone, ObjectWrite)]
pub struct IccInfo {
#[pdf(key="N")]
pub components: u32,
#[pdf(key="Alternate")]
pub alternate: Option<Box<ColorSpace>>,
#[pdf(key="Range")]
pub range: Option<Vec<f32>>,
#[pdf(key="Metadata")]
pub metadata: Option<Stream<()>>,
}
#[derive(Debug, Clone, DeepClone)]
pub enum ColorSpace {
DeviceGray,
DeviceRGB,
DeviceCMYK,
DeviceN { names: Vec<Name>, alt: Box<ColorSpace>, tint: Function, attr: Option<Dictionary> },
CalGray(Dictionary),
CalRGB(Dictionary),
CalCMYK(Dictionary),
Indexed(Box<ColorSpace>, u8, Arc<[u8]>),
Separation(Name, Box<ColorSpace>, Function),
Icc(RcRef<Stream<IccInfo>>),
Pattern,
Named(Name),
Other(Vec<Primitive>)
}
impl DataSize for ColorSpace {
const IS_DYNAMIC: bool = true;
const STATIC_HEAP_SIZE: usize = 0;
#[inline]
fn estimate_heap_size(&self) -> usize {
match *self {
ColorSpace::DeviceGray | ColorSpace::DeviceRGB | ColorSpace::DeviceCMYK => 0,
ColorSpace::DeviceN { ref names, ref alt, ref tint, ref attr } => {
names.estimate_heap_size() +
alt.estimate_heap_size() +
tint.estimate_heap_size() +
attr.estimate_heap_size()
}
ColorSpace::CalGray(ref d) | ColorSpace::CalRGB(ref d) | ColorSpace::CalCMYK(ref d) => {
d.estimate_heap_size()
}
ColorSpace::Indexed(ref cs, _, ref data) => {
cs.estimate_heap_size() + data.estimate_heap_size()
}
ColorSpace::Separation(ref name, ref cs, ref f) => {
name.estimate_heap_size() + cs.estimate_heap_size() + f.estimate_heap_size()
}
ColorSpace::Icc(ref s) => s.estimate_heap_size(),
ColorSpace::Pattern => 0,
ColorSpace::Other(ref v) => v.estimate_heap_size(),
ColorSpace::Named(ref n) => n.estimate_heap_size()
}
}
}
fn get_index(arr: &[Primitive], idx: usize) -> Result<&Primitive> {
arr.get(idx).ok_or(PdfError::Bounds { index: idx, len: arr.len() })
}
impl Object for ColorSpace {
fn from_primitive(p: Primitive, resolve: &impl Resolve) -> Result<ColorSpace> {
ColorSpace::from_primitive_depth(p, resolve, 5)
}
}
impl ColorSpace {
fn from_primitive_depth(p: Primitive, resolve: &impl Resolve, depth: usize) -> Result<ColorSpace> {
let p = p.resolve(resolve)?;
if let Ok(name) = p.as_name() {
let cs = match name {
"DeviceGray" => ColorSpace::DeviceGray,
"DeviceRGB" => ColorSpace::DeviceRGB,
"DeviceCMYK" => ColorSpace::DeviceCMYK,
"Pattern" => ColorSpace::Pattern,
name => ColorSpace::Named(name.into()),
};
return Ok(cs);
}
let arr = t!(p.into_array());
let typ_p = t!(get_index(&arr, 0)).clone().resolve(resolve)?;
let typ = t!(typ_p.as_name());
if depth == 0 {
bail!("ColorSpace base recursion");
}
match typ {
"Indexed" => {
let base = Box::new(t!(ColorSpace::from_primitive_depth(t!(get_index(&arr, 1)).clone(), resolve, depth-1)));
let hival = t!(t!(get_index(&arr, 2)).as_u8());
let lookup = match t!(get_index(&arr, 3)) {
&Primitive::Reference(r) => resolve.resolve(r)?,
p => p.clone()
};
let lookup = match lookup {
Primitive::String(string) => {
let data: Vec<u8> = string.into_bytes().into();
data.into()
}
Primitive::Stream(stream) => {
let s: Stream::<()> = Stream::from_stream(stream, resolve)?;
t!(s.data(resolve))
},
p => return Err(PdfError::UnexpectedPrimitive {
expected: "String or Stream",
found: p.get_debug_name()
})
};
Ok(ColorSpace::Indexed(base, hival, lookup))
}
"Separation" => {
let name = t!(t!(get_index(&arr, 1)).clone().into_name());
let alternate = Box::new(t!(ColorSpace::from_primitive_depth(t!(get_index(&arr, 2)).clone(), resolve, depth-1)));
let tint = t!(Function::from_primitive(t!(get_index(&arr, 3)).clone(), resolve));
Ok(ColorSpace::Separation(name, alternate, tint))
}
"ICCBased" => {
let s = t!(RcRef::from_primitive(t!(get_index(&arr, 1)).clone(), resolve));
Ok(ColorSpace::Icc(s))
}
"DeviceN" => {
let names = t!(Object::from_primitive(t!(get_index(&arr, 1)).clone(), resolve));
let alt = t!(Object::from_primitive(t!(get_index(&arr, 2)).clone(), resolve));
let tint = t!(Function::from_primitive(t!(get_index(&arr, 3)).clone(), resolve));
let attr = arr.get(4).map(|p| Dictionary::from_primitive(p.clone(), resolve)).transpose()?;
Ok(ColorSpace::DeviceN { names, alt, tint, attr})
}
"CalGray" => {
let dict = Dictionary::from_primitive(t!(get_index(&arr, 1)).clone(), resolve)?;
Ok(ColorSpace::CalGray(dict))
}
"CalRGB" => {
let dict = Dictionary::from_primitive(t!(get_index(&arr, 1)).clone(), resolve)?;
Ok(ColorSpace::CalRGB(dict))
}
"CalCMYK" => {
let dict = Dictionary::from_primitive(t!(get_index(&arr, 1)).clone(), resolve)?;
Ok(ColorSpace::CalCMYK(dict))
}
"Pattern" => {
Ok(ColorSpace::Pattern)
}
_ => Ok(ColorSpace::Other(arr))
}
}
}
impl ObjectWrite for ColorSpace {
fn to_primitive(&self, update: &mut impl Updater) -> Result<Primitive> {
match *self {
ColorSpace::DeviceCMYK => Ok(Primitive::name("DeviceCMYK")),
ColorSpace::DeviceRGB => Ok(Primitive::name("DeviceRGB")),
ColorSpace::Indexed(ref base, hival, ref lookup) => {
let base = base.to_primitive(update)?;
let hival = Primitive::Integer(hival.into());
let lookup = if lookup.len() < 100 {
PdfString::new((**lookup).into()).into()
} else {
Stream::new((), lookup.clone()).to_primitive(update)?
};
Ok(Primitive::Array(vec![Primitive::name("Indexed"), base, hival, lookup]))
}
ref p => {
dbg!(p);
unimplemented!()
}
}
}
}
主要结构
IccInfo 结构体
rust
#[derive(Object, Debug, DataSize, DeepClone, ObjectWrite)]
pub struct IccInfo {
#[pdf(key="N")]
pub components: u32,
#[pdf(key="Alternate")]
pub alternate: Option<Box<ColorSpace>>,
#[pdf(key="Range")]
pub range: Option<Vec<f32>>,
#[pdf(key="Metadata")]
pub metadata: Option<Stream<()>>,
}
功能:
- 存储ICC颜色配置文件的元信息
- 支持PDF对象序列化/反序列化
- 包含组件数量、备用颜色空间、数值范围和元数据
ColorSpace 枚举
rust
#[derive(Debug, Clone, DeepClone)]
pub enum ColorSpace {
DeviceGray,
DeviceRGB,
DeviceCMYK,
DeviceN { names: Vec<Name>, alt: Box<ColorSpace>, tint: Function, attr: Option<Dictionary> },
CalGray(Dictionary),
CalRGB(Dictionary),
CalCMYK(Dictionary),
Indexed(Box<ColorSpace>, u8, Arc<[u8]>),
Separation(Name, Box<ColorSpace>, Function),
Icc(RcRef<Stream<IccInfo>>),
Pattern,
Named(Name),
Other(Vec<Primitive>)
}
颜色空间类型详解
设备颜色空间
- DeviceGray - 设备灰度空间
- DeviceRGB - 设备RGB颜色空间
- DeviceCMYK - 设备CMYK颜色空间
特殊颜色空间
-
DeviceN - 多组件设备颜色空间
names: 颜色组件名称alt: 备用颜色空间tint: 色调变换函数attr: 附加属性
-
Indexed - 索引颜色空间
- 基础颜色空间
- 最大索引值(hival)
- 颜色查找表数据
-
Separation - 专色分离
- 颜色名称
- 备用颜色空间
- 色调变换函数
基于标准的颜色空间
- CalGray - 校准灰度空间(带校准字典)
- CalRGB - 校准RGB空间(带校准字典)
- CalCMYK - 校准CMYK空间(带校准字典)
其他类型
- Icc - ICC基于的颜色空间
- Pattern - 图案颜色空间
- Named - 命名颜色空间
- Other - 其他未实现类型
核心功能实现
内存大小估算
rust
impl DataSize for ColorSpace {
const IS_DYNAMIC: bool = true;
const STATIC_HEAP_SIZE: usize = 0;
fn estimate_heap_size(&self) -> usize {
// 为每种变体递归估算堆内存使用
match *self {
ColorSpace::DeviceGray | ColorSpace::DeviceRGB | ColorSpace::DeviceCMYK => 0,
ColorSpace::DeviceN { ref names, ref alt, ref tint, ref attr } => {
names.estimate_heap_size() +
alt.estimate_heap_size() +
tint.estimate_heap_size() +
attr.estimate_heap_size()
}
// ... 其他变体的内存估算
}
}
}
PDF对象解析
rust
impl Object for ColorSpace {
fn from_primitive(p: Primitive, resolve: &impl Resolve) -> Result<ColorSpace> {
ColorSpace::from_primitive_depth(p, resolve, 5)
}
}
解析特性:
- 递归解析,最大深度为5层防止无限递归
- 支持从名称或数组格式解析颜色空间
- 处理索引颜色空间的查找表数据(字符串或流格式)
主要解析逻辑
- 名称解析:直接映射标准颜色空间名称
- 数组解析:根据第一个元素识别颜色空间类型
- 递归处理:处理嵌套的颜色空间定义
- 错误处理:完善的边界检查和错误类型
序列化支持
rust
impl ObjectWrite for ColorSpace {
fn to_primitive(&self, update: &mut impl Updater) -> Result<Primitive> {
match *self {
ColorSpace::DeviceCMYK => Ok(Primitive::name("DeviceCMYK")),
ColorSpace::DeviceRGB => Ok(Primitive::name("DeviceRGB")),
ColorSpace::Indexed(ref base, hival, ref lookup) => {
// 将索引颜色空间转换为PDF数组格式
let base = base.to_primitive(update)?;
let hival = Primitive::Integer(hival.into());
let lookup = if lookup.len() < 100 {
PdfString::new((**lookup).into()).into()
} else {
Stream::new((), lookup.clone()).to_primitive(update)?
};
Ok(Primitive::Array(vec![Primitive::name("Indexed"), base, hival, lookup]))
}
// ... 其他变体的序列化
}
}
}
设计特点
- 完整的PDF标准支持:实现了PDF规范中定义的所有颜色空间类型
- 递归解析:支持嵌套颜色空间定义
- 内存管理:集成 datasize 用于内存使用统计
- 错误处理:详细的错误信息和边界检查
- 序列化支持:支持将颜色空间写回PDF格式
- 灵活性 :通过
Other变体支持未明确实现的颜色空间
该模块是一个完整的PDF颜色空间处理系统,专注于PDF文件的解析和生成,是PDF标准的完整实现。