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
//! Current transformation matrix, for transforming shapes (rotate, translate, scale)

use lopdf;
use lopdf::content::Operation;
use {Mm, Pt};

/// PDF "current transformation matrix". Once set, will operate on all following shapes,
/// until the `layer.restore_graphics_state()` is called. It is important to
/// call `layer.save_graphics_state()` earlier.
#[derive(Debug, Copy, Clone)]
pub enum CurTransMat {
    /// Translation matrix (in points from bottom left corner)
    /// X and Y can have different values
    Translate(Mm, Mm),
    /// Rotation matrix (clockwise, in degrees)
    Rotate(f64),
    /// Scale matrix (1.0 = 100% scale, no change)
    /// X and Y can have different values
    Scale(f64, f64),
    /// Identity matrix
    Identity,
}

/// Text matrix. Text placement is a bit different, but uses the same
/// concepts as a CTM that's why it's merged here
///
/// Note: `TextScale` does not exist. Use `layer.set_word_spacing()`
/// and `layer.set_character_spacing()` to specify the scaling between words
/// and characters.
#[derive(Debug, Copy, Clone)]
pub enum TextMatrix {
    /// Text rotation matrix, used for rotating text
    Rotate(f64),
    /// Text translate matrix, used for indenting (transforming) text
    /// (different to regular text placement)
    Translate(Mm, Mm),
}

impl Into<[f64; 6]> for TextMatrix {
    fn into(self)
    -> [f64; 6]
    {
        use TextMatrix::*;
        match self {
            Translate(x, y) => { 
                // 1 0 0 1 x y cm 
                let x_pt: Pt = x.into();
                let y_pt: Pt = y.into();
                [ 1.0, 0.0, 0.0, 1.0, x_pt.0, y_pt.0 ] 
            }
            Rotate(rot) => { let rad = (360.0 - rot).to_radians(); [rad.cos(), -rad.sin(), rad.sin(), rad.cos(), 0.0, 0.0 ] /* cos sin -sin cos 0 0 cm */ }
        }
    }
}

impl Into<[f64; 6]> for CurTransMat {
    fn into(self)
    -> [f64; 6]
    {
        use CurTransMat::*;
        match self {
            Translate(x, y) => { 
                // 1 0 0 1 x y cm 
                let x_pt: Pt = x.into();
                let y_pt: Pt = y.into();
                [ 1.0, 0.0, 0.0, 1.0, x_pt.0, y_pt.0 ]   
            }
            Rotate(rot) => { 
                // cos sin -sin cos 0 0 cm 
                let rad = (360.0 - rot).to_radians(); 
                [rad.cos(), -rad.sin(), rad.sin(), rad.cos(), 0.0, 0.0 ] 
            }
            Scale(x, y) => { 
                // x 0 0 y 0 0 cm
                [ x, 0.0, 0.0, y, 0.0, 0.0 ] 
            }
            Identity => { 
                [ 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 ] 
            }
        }
    }
}

impl Into<Operation> for CurTransMat {
	fn into(self)
	-> Operation
	{
		use lopdf::Object::*;
        let matrix_nums: [f64; 6] = self.into();
        let matrix: Vec<lopdf::Object> = matrix_nums.to_vec().into_iter().map(Real).collect();
        Operation::new("cm", matrix)
	}
}

impl Into<Operation> for TextMatrix {
    fn into(self)
    -> Operation
    {
        use lopdf::Object::*;
        let matrix_nums: [f64; 6] = self.into();
        let matrix: Vec<lopdf::Object> = matrix_nums.to_vec().into_iter().map(Real).collect();
        Operation::new("Tm", matrix)
    }
}

impl Into<lopdf::Object> for CurTransMat {
    fn into(self)
    -> lopdf::Object
    {
        use lopdf::Object::*;
        let matrix_nums: [f64; 6] = self.into();
        Array(matrix_nums.to_vec().into_iter().map(Real).collect())
    }
}

#[test]
fn test_ctm_translate()
{
    use self::*;

    // test that the translation matrix look like what PDF expects
    let ctm_trans = CurTransMat::Translate(Mm(150.0), Mm(50.0));
    let ctm_trans_arr: [f64; 6] = ctm_trans.into();
    assert_eq!([1.0_f64, 0.0, 0.0, 1.0, 425.1969, 141.7323], ctm_trans_arr);

    let ctm_scale = CurTransMat::Scale(2.0, 4.0);
    let ctm_scale_arr: [f64; 6] = ctm_scale.into();
    assert_eq!([2.0_f64, 0.0, 0.0, 4.0, 0.0, 0.0], ctm_scale_arr);

    let ctm_rot = CurTransMat::Rotate(30.0);
    let ctm_rot_arr: [f64; 6] = ctm_rot.into();
    assert_eq!([0.8660254037844384, 0.5000000000000004, -0.5000000000000004, 0.8660254037844384, 0.0, 0.0], ctm_rot_arr);
}