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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
//! PDF page management

use lopdf;
use std::rc::Weak;
use std::cell::RefCell;

use indices::{PdfPageIndex, PdfLayerIndex};
use {
    PdfResources, PdfLayer, PdfDocument, ExtendedGraphicsState, ExtendedGraphicsStateRef, Pattern, XObject, XObjectRef,
    PdfLayerReference, PatternRef, Mm, Pt
};

/// PDF page
#[derive(Debug, Clone)]
pub struct PdfPage {
    /// The index of the page in the document
    pub(crate) index: usize,
    /// page width in point
    pub width: Pt,
    /// page height in point
    pub height: Pt,
    /// Page layers
    pub layers: Vec<PdfLayer>,
    /// Resources used in this page
    pub(crate) resources: PdfResources,
}

/// A "reference" to the current page, allows for inner mutability
/// but only inside this library
pub struct PdfPageReference {
    /// A weak reference to the document, for inner mutability
    pub document: Weak<RefCell<PdfDocument>>,
    /// The index of the page this layer is on
    pub page: PdfPageIndex,
}

impl PdfPage {

    /// Create a new page, notice that width / height are in millimeter.
    /// Page must contain at least one layer
    #[inline]
    pub fn new<S>(width: Mm,
                  height: Mm,
                  layer_name: S,
                  page_index: usize)
    -> (Self, PdfLayerIndex) where S: Into<String>
    {
        let mut page = Self {
            index: page_index,
            width: width.into(),
            height: height.into(),
            layers: Vec::new(),
            resources: PdfResources::new(),
        };

        let initial_layer = PdfLayer::new(layer_name);
        page.layers.push(initial_layer);

        let layer_index = page.layers.len() - 1;

        (page, PdfLayerIndex(layer_index))
    }

    /// Iterates through the layers attached to this page and gathers all resources,
    /// which the layers need. Then returns a dictonary with all the resources
    /// (fonts, image XObjects, etc.)
    ///
    /// While originally I had planned to build a system where you can reference contents
    /// from all over the document, this turned out to be a problem, because each type had
    /// to be handled differently (PDF weirdness)
    ///
    /// `layers` should be a Vec with all layers (optional content groups) that were added
    /// to the document on a document level, it should contain the indices of the layers
    /// (they will be ignored, todo) and references to the actual OCG dictionaries
    #[inline]
    pub(crate) fn collect_resources_and_streams(self, doc: &mut lopdf::Document, layers: &[(usize, lopdf::Object)])
    -> (lopdf::Dictionary, Vec<lopdf::Stream>)
    {
        let cur_layers = layers.iter().map(|l| l.1.clone()).collect();
        let (resource_dictionary, ocg_refs) = self.resources.into_with_document_and_layers(doc, cur_layers);

        // set contents
        let mut layer_streams = Vec::<lopdf::Stream>::new();
        use lopdf::content::Operation;
        use lopdf::Object::*;

        for (idx, mut layer) in self.layers.into_iter().enumerate() {

            // push OCG and q to the beginning of the layer
            layer.operations.insert(0, Operation::new("q".into(), vec![]));
            layer.operations.insert(0, Operation::new("BDC".into(), vec![
                Name("OC".into()),
                Name(ocg_refs[idx].name.clone().into())
            ]));

            // push OCG END and Q to the end of the layer stream
            layer.operations.push(Operation::new("Q".into(), vec![]));
            layer.operations.push(Operation::new("EMC".into(), vec![]));

            // should end up looking like this:

            // /OC /MC0 BDC
            // q
            // <layer stream content>
            // Q
            // EMC

            let layer_stream = layer.into();
            layer_streams.push(layer_stream);
        }

        (resource_dictionary, layer_streams)
    }

    /// Change the graphics state. Before this operation is done, you should save
    /// the graphics state using the `save_graphics_state()` function. This will change the
    /// current graphics state until the end of the page or until the page is reset to the
    /// previous state.
    /// Returns the old graphics state, in case it was overwritten, as well as a reference
    /// to the currently active graphics state
    #[inline]
    pub fn add_graphics_state(&mut self, added_state: ExtendedGraphicsState)
    -> ExtendedGraphicsStateRef
    {
        self.resources.add_graphics_state(added_state)
    }

    /// __STUB__: Adds a pattern to the pages resources
    #[inline]
    pub fn add_pattern(&mut self, pattern: Pattern)
    -> PatternRef
    {
        self.resources.add_pattern(pattern)
    }

    /// __STUB__: Adds an XObject to the pages resources.
    /// __NOTE__: Watch out for scaling. Your XObject might be invisible or only 1pt x 1pt big
    #[inline]
    pub fn add_xobject(&mut self, xobj: XObject)
    -> XObjectRef
    {
        self.resources.add_xobject(xobj)
    }
}

impl PdfPageReference {

    /// Adds a page and returns the index of the currently added page
    #[inline]
    pub fn add_layer<S>(&self, layer_name: S)
    -> PdfLayerReference where S: Into<String>
    {
        let doc = self.document.upgrade().unwrap();
        let mut doc = doc.borrow_mut();
        let page = &mut doc.pages[self.page.0];

        let current_page_index = page.layers.len(); /* order is important */
        let layer = PdfLayer::new(layer_name);
        page.layers.push(layer);
        let index = PdfLayerIndex(current_page_index);

        PdfLayerReference {
            document: self.document.clone(),
            page: self.page,
            layer: index,
        }
    }

    /// Validates that a layer is present and returns a reference to it
    #[inline]
    #[cfg_attr(feature = "cargo-clippy", allow(no_effect))]
    pub fn get_layer(&self, layer: PdfLayerIndex)
    -> PdfLayerReference
    {
        let doc = self.document.upgrade().unwrap();
        let doc = doc.borrow();

        let _ = &doc.pages[self.page.0].layers[layer.0];

        PdfLayerReference {
            document: self.document.clone(),
            page: self.page,
            layer: layer,
        }
    }
}