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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::path::PathBuf;

// self
use geom::*;
use super::attribute::*;


/// Node's kind.
#[allow(missing_docs)]
pub enum NodeKind {
    Svg(Svg),
    Defs,
    LinearGradient(LinearGradient),
    RadialGradient(RadialGradient),
    Stop(Stop),
    ClipPath(ClipPath),
    Mask(Mask),
    Pattern(Pattern),
    Path(Path),
    Text(Text),
    TextChunk(TextChunk),
    TSpan(Box<TSpan>), // We are using Box because TSpan is too big.
    Image(Image),
    Group(Group),
}

impl NodeKind {
    /// Returns node's ID.
    ///
    /// If a current node doesn't support ID - an empty string
    /// will be returned.
    pub fn id(&self) -> &str {
        match *self {
            NodeKind::Svg(_) => "",
            NodeKind::Defs => "",
            NodeKind::LinearGradient(ref e) => e.id.as_str(),
            NodeKind::RadialGradient(ref e) => e.id.as_str(),
            NodeKind::Stop(_) => "",
            NodeKind::ClipPath(ref e) => e.id.as_str(),
            NodeKind::Mask(ref e) => e.id.as_str(),
            NodeKind::Pattern(ref e) => e.id.as_str(),
            NodeKind::Path(ref e) => e.id.as_str(),
            NodeKind::Text(ref e) => e.id.as_str(),
            NodeKind::TextChunk(_) => "",
            NodeKind::TSpan(_) => "",
            NodeKind::Image(ref e) => e.id.as_str(),
            NodeKind::Group(ref e) => e.id.as_str(),
        }
    }

    /// Returns node's transform.
    ///
    /// If a current node doesn't support transformation - a default
    /// transform will be returned.
    pub fn transform(&self) -> Transform {
        match *self {
            NodeKind::Svg(_) => Transform::default(),
            NodeKind::Defs => Transform::default(),
            NodeKind::LinearGradient(ref e) => e.d.transform,
            NodeKind::RadialGradient(ref e) => e.d.transform,
            NodeKind::Stop(_) => Transform::default(),
            NodeKind::ClipPath(ref e) => e.transform,
            NodeKind::Mask(_) => Transform::default(),
            NodeKind::Pattern(ref e) => e.transform,
            NodeKind::Path(ref e) => e.transform,
            NodeKind::Text(ref e) => e.transform,
            NodeKind::TextChunk(_) => Transform::default(),
            NodeKind::TSpan(_) => Transform::default(),
            NodeKind::Image(ref e) => e.transform,
            NodeKind::Group(ref e) => e.transform,
        }
    }
}


/// An SVG root element.
#[derive(Clone, Copy, Debug)]
pub struct Svg {
    /// Image size.
    ///
    /// Size of an image that should be created to fit the SVG.
    ///
    /// `width` and `height` in the SVG.
    pub size: Size,
    /// SVG viewbox.
    ///
    /// Specifies which part of the SVG image should be rendered.
    ///
    /// `viewBox` and `preserveAspectRatio` in the SVG.
    pub view_box: ViewBox,
}


/// A path element.
#[derive(Clone)]
pub struct Path {
    /// Element's ID.
    ///
    /// Taken from the SVG itself.
    /// Isn't automatically generated.
    /// Can be empty.
    pub id: String,
    /// Element transform.
    pub transform: Transform,
    /// Fill style.
    pub fill: Option<Fill>,
    /// Stroke style.
    pub stroke: Option<Stroke>,
    /// Segments list.
    ///
    /// All segments are in absolute coordinates.
    pub segments: Vec<PathSegment>,
}


/// A text element.
///
/// `text` element in the SVG.
pub struct Text {
    /// Element's ID.
    ///
    /// Taken from the SVG itself.
    /// Isn't automatically generated.
    /// Can be empty.
    pub id: String,
    /// Element transform.
    pub transform: Transform,
    /// Rotate
    pub rotate: Option<NumberList>,
}


/// A text chunk.
///
/// Contains position and anchor of the next
/// [text chunk](https://www.w3.org/TR/SVG11/text.html#TextChunk).
///
/// Doesn't represented in the SVG directly. Usually, it's a first `tspan` or text node
/// and any `tspan` that defines either `x` or `y` coordinates.
#[derive(Clone)]
pub struct TextChunk {
    /// A list of absolute positions along the X-axis.
    pub x: Option<NumberList>,
    /// A list of absolute positions along the Y-axis.
    pub y: Option<NumberList>,
    /// A list of relative positions along the X-axis.
    pub dx: Option<NumberList>,
    /// A list of relative positions along the Y-axis.
    pub dy: Option<NumberList>,
    /// A text anchor/align.
    pub anchor: TextAnchor,
}


// TODO: dx, dy
/// A text span.
///
/// `tspan` element in the SVG.
#[derive(Clone)]
pub struct TSpan {
    /// Fill style.
    pub fill: Option<Fill>,
    /// Stroke style.
    pub stroke: Option<Stroke>,
    /// Font description.
    pub font: Font,
    /// Text decoration.
    ///
    /// Unlike `text-decoration` attribute from the SVG, this one has all styles resolved.
    /// Basically, by the SVG `text-decoration` attribute can be defined on `tspan` element
    /// and on any parent element. And all definitions should be combined.
    /// The one that was defined by `tspan` uses the `tspan` style itself.
    /// The one that was defined by any parent node uses the `text` element style.
    /// So it's pretty hard to resolve.
    ///
    /// This property has all this stuff resolved.
    pub decoration: TextDecoration,
    /// An actual text line.
    ///
    /// SVG doesn't support multiline text, so this property doesn't have a new line inside of it.
    /// All the spaces are already trimmed or preserved, depending on the `xml:space` attribute.
    /// All characters references are already resolved, so there is no `&gt;` or `&#x50;`.
    /// So this text should be rendered as is, without any postprocessing.
    pub text: String,
}


/// A raster image element.
///
/// `image` element in the SVG.
pub struct Image {
    /// Element's ID.
    ///
    /// Taken from the SVG itself.
    /// Isn't automatically generated.
    /// Can be empty.
    pub id: String,
    /// Element transform.
    pub transform: Transform,
    /// An image rectangle in which it should be fit.
    ///
    /// Combination of the `x`, `y`, `width`, `height` and `preserveAspectRatio`
    /// attributes.
    pub view_box: ViewBox,
    /// Image data.
    pub data: ImageData,
    /// Image data kind.
    pub format: ImageFormat,
}


/// A raster image container.
pub enum ImageData {
    /// Path to a PNG, JPEG or SVG(Z) image.
    ///
    /// Preprocessor checks that file exists, but because it can be removed later,
    /// there is no guarantee that this path is valid.
    Path(PathBuf),
    /// An image raw data.
    ///
    /// It's not a decoded image data, but the data that was decoded from base64.
    /// So you still need a PNG, JPEG and SVG(Z) decoding library.
    Raw(Vec<u8>),
}


/// An image codec.
#[allow(missing_docs)]
#[derive(Clone, Copy, PartialEq)]
pub enum ImageFormat {
    PNG,
    JPEG,
    SVG,
}


/// A group container.
///
/// The preprocessor will remove all groups that don't impact rendering.
/// Those that left is just an indicator that a new canvas should be created.
///
/// `g` element in the SVG.
pub struct Group {
    /// Element's ID.
    ///
    /// Taken from the SVG itself.
    /// Isn't automatically generated.
    /// Can be empty.
    pub id: String,
    /// Element transform.
    pub transform: Transform,
    /// Group opacity.
    ///
    /// After the group is rendered we should combine
    /// it with a parent group using the specified opacity.
    pub opacity: Option<Opacity>,
    /// Element clip path.
    pub clip_path: Option<String>,
    /// Element mask.
    pub mask: Option<String>,
}


/// A generic gradient.
#[derive(Clone, Copy)]
pub struct BaseGradient {
    /// Coordinate system units.
    ///
    /// `gradientUnits` in the SVG.
    pub units: Units,
    /// Gradient transform.
    ///
    /// `gradientTransform` in the SVG.
    pub transform: Transform,
    /// Gradient spreading method.
    ///
    /// `spreadMethod` in the SVG.
    pub spread_method: SpreadMethod,
}


/// A linear gradient.
///
/// `linearGradient` element in the SVG.
#[allow(missing_docs)]
pub struct LinearGradient {
    /// Element's ID.
    ///
    /// Taken from the SVG itself.
    /// Can't be empty.
    pub id: String,
    pub x1: f64,
    pub y1: f64,
    pub x2: f64,
    pub y2: f64,
    /// Base gradient data.
    pub d: BaseGradient,
}


/// A radial gradient.
///
/// `radialGradient` element in the SVG.
#[allow(missing_docs)]
pub struct RadialGradient {
    /// Element's ID.
    ///
    /// Taken from the SVG itself.
    /// Can't be empty.
    pub id: String,
    pub cx: f64,
    pub cy: f64,
    pub r: f64,
    pub fx: f64,
    pub fy: f64,
    /// Base gradient data.
    pub d: BaseGradient,
}


/// Gradient's stop element.
///
/// `stop` element in the SVG.
#[derive(Clone, Copy)]
#[allow(missing_docs)]
pub struct Stop {
    pub offset: StopOffset,
    pub color: Color,
    pub opacity: Opacity,
}


/// A clip-path element.
///
/// `clipPath` element in the SVG.
pub struct ClipPath {
    /// Element's ID.
    ///
    /// Taken from the SVG itself.
    /// Can't be empty.
    pub id: String,
    /// Coordinate system units.
    ///
    /// `clipPathUnits` in the SVG.
    pub units: Units,
    /// Clip path transform.
    ///
    /// `transform` in the SVG.
    pub transform: Transform,
}


/// A mask element.
///
/// `mask` element in the SVG.
pub struct Mask {
    /// Element's ID.
    ///
    /// Taken from the SVG itself.
    /// Can't be empty.
    pub id: String,
    /// Coordinate system units.
    ///
    /// `maskUnits` in the SVG.
    pub units: Units,
    /// Content coordinate system units.
    ///
    /// `maskContentUnits` in the SVG.
    pub content_units: Units,
    /// Pattern rectangle.
    ///
    /// `x`, `y`, `width` and `height` in the SVG.
    pub rect: Rect,
}


/// A pattern element.
///
/// `pattern` element in the SVG.
pub struct Pattern {
    /// Element's ID.
    ///
    /// Taken from the SVG itself.
    /// Can't be empty.
    pub id: String,
    /// Coordinate system units.
    ///
    /// `patternUnits` in the SVG.
    pub units: Units,
    // TODO: should not be accessible when `viewBox` is present.
    /// Content coordinate system units.
    ///
    /// `patternContentUnits` in the SVG.
    pub content_units: Units,
    /// Pattern transform.
    ///
    /// `patternTransform` in the SVG.
    pub transform: Transform,
    /// Pattern rectangle.
    ///
    /// `x`, `y`, `width` and `height` in the SVG.
    pub rect: Rect,
    /// Pattern viewbox.
    pub view_box: Option<ViewBox>,
}