【pdf-rs】color.rs 文件解析

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层防止无限递归
  • 支持从名称或数组格式解析颜色空间
  • 处理索引颜色空间的查找表数据(字符串或流格式)

主要解析逻辑

  1. 名称解析:直接映射标准颜色空间名称
  2. 数组解析:根据第一个元素识别颜色空间类型
  3. 递归处理:处理嵌套的颜色空间定义
  4. 错误处理:完善的边界检查和错误类型

序列化支持

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]))
            }
            // ... 其他变体的序列化
        }
    }
}

设计特点

  1. 完整的PDF标准支持:实现了PDF规范中定义的所有颜色空间类型
  2. 递归解析:支持嵌套颜色空间定义
  3. 内存管理:集成 datasize 用于内存使用统计
  4. 错误处理:详细的错误信息和边界检查
  5. 序列化支持:支持将颜色空间写回PDF格式
  6. 灵活性 :通过 Other 变体支持未明确实现的颜色空间

该模块是一个完整的PDF颜色空间处理系统,专注于PDF文件的解析和生成,是PDF标准的完整实现。

相关推荐
ceffans13 小时前
PDF文档中表格以及形状解析-后续处理(线段生成最小多边形)
c++·windows·算法·pdf
Source.Liu13 小时前
【printpdf】color.rs 文件解析
rust·pdf
乘风!1 天前
前端Jquery,后端Java实现预览Word、Excel、PPT,pdf等文档
pdf·word·excel·jquery
我有一棵树1 天前
浏览器使用 <embed> 标签预览 PDF 的原理
pdf·embed
蜀中廖化1 天前
小技巧:ipynb转pdf
pdf·小工具·python to pdf
Eiceblue1 天前
使用 Python 向 PDF 添加附件与附件注释
linux·开发语言·vscode·python·pdf
蛋王派1 天前
本地部署DeepSeek-OCR:打造高效的PDF文字识别服务
人工智能·自然语言处理·pdf·ocr
Iloveskr1 天前
markdown转为pdf导出
java·pdf
Source.Liu2 天前
【PDF】PDF文件体详解
pdf