1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//! ICC profile that can be embedded into a PDF

extern crate lopdf;

/// Type of the icc profile
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum IccProfileType {
    Cmyk,
    Rgb,
    Greyscale,
}

/// Icc profile
#[derive(Debug, Clone, PartialEq)]
pub struct IccProfile {
    /// Binary Icc profile
    icc: Vec<u8>,
    /// CMYK or RGB or LAB icc profile?
    icc_type: IccProfileType,
    /// Does the ICC profile have an "Alternate" version or not?
    pub has_alternate: bool,
    /// Does the ICC profile have an "Range" dictionary
    /// Really not sure why this is needed, but this is needed on the documents Info dictionary
    pub has_range: bool,
}

impl IccProfile {
    /// Creates a new Icc Profile
    pub fn new(icc: Vec<u8>, icc_type: IccProfileType)
    -> Self
    {
        Self {
            icc: icc,
            icc_type: icc_type,
            has_alternate: true,
            has_range: false,
        }
    }

    /// Does the ICC profile have an alternate version (such as "DeviceCMYk")?
    #[inline]
    pub fn with_alternate_profile(mut self, has_alternate: bool)
    -> Self
    {
        self.has_alternate = has_alternate;
        self
    }

    /// Does the ICC profile have an "Range" dictionary?
    #[inline]
    pub fn with_range(mut self, has_range: bool)
    -> Self
    {
        self.has_range = has_range;
        self
    }

}

impl Into<lopdf::Stream> for IccProfile {
    fn into(self)
    -> lopdf::Stream
    {
        use lopdf::{Dictionary as LoDictionary,
                    Stream as LoStream};
        use lopdf::Object::*;
        use std::iter::FromIterator;

        let (num_icc_fields, alternate) = match self.icc_type {
            IccProfileType::Cmyk => (4, "DeviceCMYK"),
            IccProfileType::Rgb => (3, "DeviceRGB"),
            IccProfileType::Greyscale => (1, "DeviceGray"),
        };

        let mut stream_dict = LoDictionary::from_iter(vec![
                ("N", Integer(num_icc_fields)).into(),
                ("Length", Integer(self.icc.len() as i64).into())]);

        if self.has_alternate {
            stream_dict.set("Alternate", Name(alternate.into()));
        }

        if self.has_range {
            stream_dict.set("Range", Array(vec![
                                        Real(0.0),
                                        Real(1.0),
                                        Real(0.0),
                                        Real(1.0),
                                        Real(0.0),
                                        Real(1.0),
                                        Real(0.0),
                                        Real(1.0)]));
        }

        LoStream::new(stream_dict, self.icc)
    }
}

/// Named reference for an ICC profile
#[derive(Debug, Clone, PartialEq)]
pub struct IccProfileRef {
    pub(crate) name: String,
}

impl IccProfileRef {
    /// Creates a new IccProfileRef
    pub fn new(index: usize)
    -> Self
    {
        Self {
            name: format!("/ICC{}", index)
        }
    }
}

#[derive(Default, Clone, Debug, PartialEq)]
pub struct IccProfileList {
    profiles: Vec<IccProfile>,
}

impl IccProfileList {
    /// Creates a new IccProfileList
    pub fn new()
    -> Self
    {
        Self::default()
    }

    /// Adds an ICC profile
    pub fn add_profile(&mut self, profile: IccProfile)
    -> IccProfileRef
    {
        let cur_len = self.profiles.len();
        self.profiles.push(profile);
        IccProfileRef::new(cur_len)
    }
}