1#[derive(Clone, Debug, Copy, PartialEq)]
7pub enum ShapeType {
8 Rectangle,
10 RoundedRectangle,
11 Ellipse,
12 Circle, Triangle,
14 RightTriangle,
15 Diamond,
16 Pentagon,
17 Hexagon,
18 Octagon,
19
20 RightArrow,
22 LeftArrow,
23 UpArrow,
24 DownArrow,
25 LeftRightArrow,
26 UpDownArrow,
27 BentArrow,
28 UTurnArrow,
29
30 Star4,
32 Star5,
33 Star6,
34 Star8,
35 Ribbon,
36 Wave,
37
38 WedgeRectCallout,
40 WedgeEllipseCallout,
41 CloudCallout,
42
43 FlowChartProcess,
45 FlowChartDecision,
46 FlowChartTerminator,
47 FlowChartDocument,
48 FlowChartPredefinedProcess,
49 FlowChartInternalStorage,
50 FlowChartData,
51 FlowChartInputOutput,
52 FlowChartManualInput,
53 FlowChartManualOperation,
54 FlowChartConnector,
55 FlowChartOffPageConnector,
56 FlowChartPunchedCard,
57 FlowChartPunchedTape,
58 FlowChartSummingJunction,
59 FlowChartOr,
60 FlowChartCollate,
61 FlowChartSort,
62 FlowChartExtract,
63 FlowChartMerge,
64 FlowChartOnlineStorage,
65 FlowChartDelay,
66 FlowChartMagneticTape,
67 FlowChartMagneticDisk,
68 FlowChartMagneticDrum,
69 FlowChartDisplay,
70 FlowChartPreparation,
71
72 CurvedRightArrow,
74 CurvedLeftArrow,
75 CurvedUpArrow,
76 CurvedDownArrow,
77 CurvedLeftRightArrow,
78 CurvedUpDownArrow,
79 StripedRightArrow,
80 NotchedRightArrow,
81 PentagonArrow,
82 ChevronArrow,
83 RightArrowCallout,
84 LeftArrowCallout,
85 UpArrowCallout,
86 DownArrowCallout,
87 LeftRightArrowCallout,
88 UpDownArrowCallout,
89 QuadArrow,
90 LeftRightUpArrow,
91 CircularArrow,
92
93 Parallelogram,
95 Trapezoid,
96 NonIsoscelesTrapezoid,
97 IsoscelesTrapezoid,
98 Cube,
99 Can,
100 Cone,
101 Cylinder,
102 Bevel,
103 Donut,
104 NoSmoking,
105 BlockArc,
106 FoldedCorner,
107 SmileyFace,
108 Arc,
109 Chord,
110 Pie,
111 Teardrop,
112 Plaque,
113 MusicNote,
114 PictureFrame,
115
116 Star10,
118 Star12,
119 Star16,
120 Star24,
121 Star32,
122 Seal,
123 Seal4,
124 Seal8,
125 Seal16,
126 Seal32,
127 ActionButtonBlank,
128 ActionButtonHome,
129 ActionButtonHelp,
130 ActionButtonInformation,
131 ActionButtonForwardNext,
132 ActionButtonBackPrevious,
133 ActionButtonBeginning,
134 ActionButtonEnd,
135 ActionButtonReturn,
136 ActionButtonDocument,
137 ActionButtonSound,
138 ActionButtonMovie,
139
140 Heart,
142 Lightning,
143 Sun,
144 Moon,
145 Cloud,
146 Brace,
147 Bracket,
148 Plus,
149 Minus,
150}
151
152impl ShapeType {
153 pub fn preset_name(&self) -> &'static str {
155 match self {
156 ShapeType::Rectangle => "rect",
157 ShapeType::RoundedRectangle => "roundRect",
158 ShapeType::Ellipse | ShapeType::Circle => "ellipse",
159 ShapeType::Triangle => "triangle",
160 ShapeType::RightTriangle => "rtTriangle",
161 ShapeType::Diamond => "diamond",
162 ShapeType::Pentagon => "pentagon",
163 ShapeType::Hexagon => "hexagon",
164 ShapeType::Octagon => "octagon",
165
166 ShapeType::RightArrow => "rightArrow",
167 ShapeType::LeftArrow => "leftArrow",
168 ShapeType::UpArrow => "upArrow",
169 ShapeType::DownArrow => "downArrow",
170 ShapeType::LeftRightArrow => "leftRightArrow",
171 ShapeType::UpDownArrow => "upDownArrow",
172 ShapeType::BentArrow => "bentArrow",
173 ShapeType::UTurnArrow => "uturnArrow",
174
175 ShapeType::Star4 => "star4",
176 ShapeType::Star5 => "star5",
177 ShapeType::Star6 => "star6",
178 ShapeType::Star8 => "star8",
179 ShapeType::Ribbon => "ribbon2",
180 ShapeType::Wave => "wave",
181
182 ShapeType::WedgeRectCallout => "wedgeRectCallout",
183 ShapeType::WedgeEllipseCallout => "wedgeEllipseCallout",
184 ShapeType::CloudCallout => "cloudCallout",
185
186 ShapeType::FlowChartProcess => "flowChartProcess",
187 ShapeType::FlowChartDecision => "flowChartDecision",
188 ShapeType::FlowChartTerminator => "flowChartTerminator",
189 ShapeType::FlowChartDocument => "flowChartDocument",
190 ShapeType::FlowChartPredefinedProcess => "flowChartPredefinedProcess",
191 ShapeType::FlowChartInternalStorage => "flowChartInternalStorage",
192 ShapeType::FlowChartData => "flowChartData",
193 ShapeType::FlowChartInputOutput => "flowChartInputOutput",
194 ShapeType::FlowChartManualInput => "flowChartManualInput",
195 ShapeType::FlowChartManualOperation => "flowChartManualOperation",
196 ShapeType::FlowChartConnector => "flowChartConnector",
197 ShapeType::FlowChartOffPageConnector => "flowChartOffPageConnector",
198 ShapeType::FlowChartPunchedCard => "flowChartPunchedCard",
199 ShapeType::FlowChartPunchedTape => "flowChartPunchedTape",
200 ShapeType::FlowChartSummingJunction => "flowChartSummingJunction",
201 ShapeType::FlowChartOr => "flowChartOr",
202 ShapeType::FlowChartCollate => "flowChartCollate",
203 ShapeType::FlowChartSort => "flowChartSort",
204 ShapeType::FlowChartExtract => "flowChartExtract",
205 ShapeType::FlowChartMerge => "flowChartMerge",
206 ShapeType::FlowChartOnlineStorage => "flowChartOnlineStorage",
207 ShapeType::FlowChartDelay => "flowChartDelay",
208 ShapeType::FlowChartMagneticTape => "flowChartMagneticTape",
209 ShapeType::FlowChartMagneticDisk => "flowChartMagneticDisk",
210 ShapeType::FlowChartMagneticDrum => "flowChartMagneticDrum",
211 ShapeType::FlowChartDisplay => "flowChartDisplay",
212 ShapeType::FlowChartPreparation => "flowChartPreparation",
213
214 ShapeType::CurvedRightArrow => "curvedRightArrow",
215 ShapeType::CurvedLeftArrow => "curvedLeftArrow",
216 ShapeType::CurvedUpArrow => "curvedUpArrow",
217 ShapeType::CurvedDownArrow => "curvedDownArrow",
218 ShapeType::CurvedLeftRightArrow => "curvedLeftRightArrow",
219 ShapeType::CurvedUpDownArrow => "curvedUpDownArrow",
220 ShapeType::StripedRightArrow => "stripedRightArrow",
221 ShapeType::NotchedRightArrow => "notchedRightArrow",
222 ShapeType::PentagonArrow => "pentArrow",
223 ShapeType::ChevronArrow => "chevron",
224 ShapeType::RightArrowCallout => "rightArrowCallout",
225 ShapeType::LeftArrowCallout => "leftArrowCallout",
226 ShapeType::UpArrowCallout => "upArrowCallout",
227 ShapeType::DownArrowCallout => "downArrowCallout",
228 ShapeType::LeftRightArrowCallout => "leftRightArrowCallout",
229 ShapeType::UpDownArrowCallout => "upDownArrowCallout",
230 ShapeType::QuadArrow => "quadArrow",
231 ShapeType::LeftRightUpArrow => "leftRightUpArrow",
232 ShapeType::CircularArrow => "circularArrow",
233
234 ShapeType::Parallelogram => "parallelogram",
235 ShapeType::Trapezoid => "trapezoid",
236 ShapeType::NonIsoscelesTrapezoid => "nonIsoscelesTrapezoid",
237 ShapeType::IsoscelesTrapezoid => "isoTrapezoid",
238 ShapeType::Cube => "cube",
239 ShapeType::Can => "can",
240 ShapeType::Cone => "cone",
241 ShapeType::Cylinder => "cylinder",
242 ShapeType::Bevel => "bevel",
243 ShapeType::Donut => "donut",
244 ShapeType::NoSmoking => "noSmoking",
245 ShapeType::BlockArc => "blockArc",
246 ShapeType::FoldedCorner => "foldedCorner",
247 ShapeType::SmileyFace => "smileyFace",
248 ShapeType::Arc => "arc",
249 ShapeType::Chord => "chord",
250 ShapeType::Pie => "pie",
251 ShapeType::Teardrop => "teardrop",
252 ShapeType::Plaque => "plaque",
253 ShapeType::MusicNote => "musicNote",
254 ShapeType::PictureFrame => "frame",
255
256 ShapeType::Star10 => "star10",
257 ShapeType::Star12 => "star12",
258 ShapeType::Star16 => "star16",
259 ShapeType::Star24 => "star24",
260 ShapeType::Star32 => "star32",
261 ShapeType::Seal => "seal",
262 ShapeType::Seal4 => "seal4",
263 ShapeType::Seal8 => "seal8",
264 ShapeType::Seal16 => "seal16",
265 ShapeType::Seal32 => "seal32",
266 ShapeType::ActionButtonBlank => "actionButtonBlank",
267 ShapeType::ActionButtonHome => "actionButtonHome",
268 ShapeType::ActionButtonHelp => "actionButtonHelp",
269 ShapeType::ActionButtonInformation => "actionButtonInformation",
270 ShapeType::ActionButtonForwardNext => "actionButtonForwardNext",
271 ShapeType::ActionButtonBackPrevious => "actionButtonBackPrevious",
272 ShapeType::ActionButtonBeginning => "actionButtonBeginning",
273 ShapeType::ActionButtonEnd => "actionButtonEnd",
274 ShapeType::ActionButtonReturn => "actionButtonReturn",
275 ShapeType::ActionButtonDocument => "actionButtonDocument",
276 ShapeType::ActionButtonSound => "actionButtonSound",
277 ShapeType::ActionButtonMovie => "actionButtonMovie",
278
279 ShapeType::Heart => "heart",
280 ShapeType::Lightning => "lightningBolt",
281 ShapeType::Sun => "sun",
282 ShapeType::Moon => "moon",
283 ShapeType::Cloud => "cloud",
284 ShapeType::Brace => "leftBrace",
285 ShapeType::Bracket => "leftBracket",
286 ShapeType::Plus => "mathPlus",
287 ShapeType::Minus => "mathMinus",
288 }
289 }
290
291 pub fn display_name(&self) -> &'static str {
293 match self {
294 ShapeType::Rectangle => "Rectangle",
295 ShapeType::RoundedRectangle => "Rounded Rectangle",
296 ShapeType::Ellipse => "Ellipse",
297 ShapeType::Circle => "Circle",
298 ShapeType::Triangle => "Triangle",
299 ShapeType::RightTriangle => "Right Triangle",
300 ShapeType::Diamond => "Diamond",
301 ShapeType::Pentagon => "Pentagon",
302 ShapeType::Hexagon => "Hexagon",
303 ShapeType::Octagon => "Octagon",
304 ShapeType::RightArrow => "Right Arrow",
305 ShapeType::LeftArrow => "Left Arrow",
306 ShapeType::UpArrow => "Up Arrow",
307 ShapeType::DownArrow => "Down Arrow",
308 ShapeType::LeftRightArrow => "Left-Right Arrow",
309 ShapeType::UpDownArrow => "Up-Down Arrow",
310 ShapeType::BentArrow => "Bent Arrow",
311 ShapeType::UTurnArrow => "U-Turn Arrow",
312 ShapeType::Star4 => "4-Point Star",
313 ShapeType::Star5 => "5-Point Star",
314 ShapeType::Star6 => "6-Point Star",
315 ShapeType::Star8 => "8-Point Star",
316 ShapeType::Ribbon => "Ribbon",
317 ShapeType::Wave => "Wave",
318 ShapeType::WedgeRectCallout => "Rectangle Callout",
319 ShapeType::WedgeEllipseCallout => "Oval Callout",
320 ShapeType::CloudCallout => "Cloud Callout",
321 ShapeType::FlowChartProcess => "Process",
322 ShapeType::FlowChartDecision => "Decision",
323 ShapeType::FlowChartTerminator => "Terminator",
324 ShapeType::FlowChartDocument => "Document",
325 ShapeType::FlowChartPredefinedProcess => "Predefined Process",
326 ShapeType::FlowChartInternalStorage => "Internal Storage",
327 ShapeType::FlowChartData => "Data",
328 ShapeType::FlowChartInputOutput => "Input/Output",
329 ShapeType::FlowChartManualInput => "Manual Input",
330 ShapeType::FlowChartManualOperation => "Manual Operation",
331 ShapeType::FlowChartConnector => "Connector",
332 ShapeType::FlowChartOffPageConnector => "Off-page Connector",
333 ShapeType::FlowChartPunchedCard => "Punched Card",
334 ShapeType::FlowChartPunchedTape => "Punched Tape",
335 ShapeType::FlowChartSummingJunction => "Summing Junction",
336 ShapeType::FlowChartOr => "Or",
337 ShapeType::FlowChartCollate => "Collate",
338 ShapeType::FlowChartSort => "Sort",
339 ShapeType::FlowChartExtract => "Extract",
340 ShapeType::FlowChartMerge => "Merge",
341 ShapeType::FlowChartOnlineStorage => "Online Storage",
342 ShapeType::FlowChartDelay => "Delay",
343 ShapeType::FlowChartMagneticTape => "Magnetic Tape",
344 ShapeType::FlowChartMagneticDisk => "Magnetic Disk",
345 ShapeType::FlowChartMagneticDrum => "Magnetic Drum",
346 ShapeType::FlowChartDisplay => "Display",
347 ShapeType::FlowChartPreparation => "Preparation",
348
349 ShapeType::CurvedRightArrow => "Curved Right Arrow",
350 ShapeType::CurvedLeftArrow => "Curved Left Arrow",
351 ShapeType::CurvedUpArrow => "Curved Up Arrow",
352 ShapeType::CurvedDownArrow => "Curved Down Arrow",
353 ShapeType::CurvedLeftRightArrow => "Curved Left-Right Arrow",
354 ShapeType::CurvedUpDownArrow => "Curved Up-Down Arrow",
355 ShapeType::StripedRightArrow => "Striped Right Arrow",
356 ShapeType::NotchedRightArrow => "Notched Right Arrow",
357 ShapeType::PentagonArrow => "Pentagon Arrow",
358 ShapeType::ChevronArrow => "Chevron Arrow",
359 ShapeType::RightArrowCallout => "Right Arrow Callout",
360 ShapeType::LeftArrowCallout => "Left Arrow Callout",
361 ShapeType::UpArrowCallout => "Up Arrow Callout",
362 ShapeType::DownArrowCallout => "Down Arrow Callout",
363 ShapeType::LeftRightArrowCallout => "Left-Right Arrow Callout",
364 ShapeType::UpDownArrowCallout => "Up-Down Arrow Callout",
365 ShapeType::QuadArrow => "Quad Arrow",
366 ShapeType::LeftRightUpArrow => "Left-Right-Up Arrow",
367 ShapeType::CircularArrow => "Circular Arrow",
368
369 ShapeType::Parallelogram => "Parallelogram",
370 ShapeType::Trapezoid => "Trapezoid",
371 ShapeType::NonIsoscelesTrapezoid => "Non-Isosceles Trapezoid",
372 ShapeType::IsoscelesTrapezoid => "Isosceles Trapezoid",
373 ShapeType::Cube => "Cube",
374 ShapeType::Can => "Can",
375 ShapeType::Cone => "Cone",
376 ShapeType::Cylinder => "Cylinder",
377 ShapeType::Bevel => "Bevel",
378 ShapeType::Donut => "Donut",
379 ShapeType::NoSmoking => "No Smoking",
380 ShapeType::BlockArc => "Block Arc",
381 ShapeType::FoldedCorner => "Folded Corner",
382 ShapeType::SmileyFace => "Smiley Face",
383 ShapeType::Arc => "Arc",
384 ShapeType::Chord => "Chord",
385 ShapeType::Pie => "Pie",
386 ShapeType::Teardrop => "Teardrop",
387 ShapeType::Plaque => "Plaque",
388 ShapeType::MusicNote => "Music Note",
389 ShapeType::PictureFrame => "Picture Frame",
390
391 ShapeType::Star10 => "10-Point Star",
392 ShapeType::Star12 => "12-Point Star",
393 ShapeType::Star16 => "16-Point Star",
394 ShapeType::Star24 => "24-Point Star",
395 ShapeType::Star32 => "32-Point Star",
396 ShapeType::Seal => "Seal",
397 ShapeType::Seal4 => "4-Point Seal",
398 ShapeType::Seal8 => "8-Point Seal",
399 ShapeType::Seal16 => "16-Point Seal",
400 ShapeType::Seal32 => "32-Point Seal",
401 ShapeType::ActionButtonBlank => "Action Button (Blank)",
402 ShapeType::ActionButtonHome => "Action Button (Home)",
403 ShapeType::ActionButtonHelp => "Action Button (Help)",
404 ShapeType::ActionButtonInformation => "Action Button (Information)",
405 ShapeType::ActionButtonForwardNext => "Action Button (Forward/Next)",
406 ShapeType::ActionButtonBackPrevious => "Action Button (Back/Previous)",
407 ShapeType::ActionButtonBeginning => "Action Button (Beginning)",
408 ShapeType::ActionButtonEnd => "Action Button (End)",
409 ShapeType::ActionButtonReturn => "Action Button (Return)",
410 ShapeType::ActionButtonDocument => "Action Button (Document)",
411 ShapeType::ActionButtonSound => "Action Button (Sound)",
412 ShapeType::ActionButtonMovie => "Action Button (Movie)",
413
414 ShapeType::Heart => "Heart",
415 ShapeType::Lightning => "Lightning Bolt",
416 ShapeType::Sun => "Sun",
417 ShapeType::Moon => "Moon",
418 ShapeType::Cloud => "Cloud",
419 ShapeType::Brace => "Brace",
420 ShapeType::Bracket => "Bracket",
421 ShapeType::Plus => "Plus",
422 ShapeType::Minus => "Minus",
423 }
424 }
425}
426
427#[derive(Clone, Debug, Copy, PartialEq)]
429pub enum GradientDirection {
430 Horizontal,
432 Vertical,
434 DiagonalDown,
436 DiagonalUp,
438 Angle(u32),
440}
441
442impl GradientDirection {
443 pub fn to_angle(&self) -> u32 {
445 match self {
446 GradientDirection::Horizontal => 0,
447 GradientDirection::Vertical => 5400000, GradientDirection::DiagonalDown => 2700000, GradientDirection::DiagonalUp => 18900000, GradientDirection::Angle(deg) => deg * 60000,
451 }
452 }
453}
454
455#[derive(Clone, Debug)]
457pub struct GradientStop {
458 pub color: String,
459 pub position: u32, pub transparency: Option<u32>,
461}
462
463impl GradientStop {
464 pub fn new(color: &str, position_percent: u32) -> Self {
466 GradientStop {
467 color: color.trim_start_matches('#').to_uppercase(),
468 position: position_percent.min(100) * 1000,
469 transparency: None,
470 }
471 }
472
473 pub fn with_transparency(mut self, percent: u32) -> Self {
475 let alpha = (100 - percent.min(100)) * 1000;
476 self.transparency = Some(alpha);
477 self
478 }
479}
480
481#[derive(Clone, Debug)]
483pub struct GradientFill {
484 pub stops: Vec<GradientStop>,
485 pub direction: GradientDirection,
486}
487
488impl GradientFill {
489 pub fn linear(start_color: &str, end_color: &str, direction: GradientDirection) -> Self {
491 GradientFill {
492 stops: vec![
493 GradientStop::new(start_color, 0),
494 GradientStop::new(end_color, 100),
495 ],
496 direction,
497 }
498 }
499
500 pub fn three_color(start: &str, middle: &str, end: &str, direction: GradientDirection) -> Self {
502 GradientFill {
503 stops: vec![
504 GradientStop::new(start, 0),
505 GradientStop::new(middle, 50),
506 GradientStop::new(end, 100),
507 ],
508 direction,
509 }
510 }
511
512 pub fn with_stop(mut self, stop: GradientStop) -> Self {
514 self.stops.push(stop);
515 self.stops.sort_by_key(|s| s.position);
516 self
517 }
518}
519
520#[derive(Clone, Debug)]
522pub enum FillType {
523 Solid(ShapeFill),
524 Gradient(GradientFill),
525 NoFill,
526}
527
528#[derive(Clone, Debug)]
530pub struct ShapeFill {
531 pub color: String, pub transparency: Option<u32>, }
534
535impl ShapeFill {
536 pub fn new(color: &str) -> Self {
538 ShapeFill {
539 color: color.trim_start_matches('#').to_uppercase(),
540 transparency: None,
541 }
542 }
543
544 pub fn with_transparency(mut self, percent: u32) -> Self {
546 let alpha = (100 - percent.min(100)) * 1000;
547 self.transparency = Some(alpha);
548 self
549 }
550
551 pub fn transparency(self, percent: u32) -> Self {
553 self.with_transparency(percent)
554 }
555}
556
557#[derive(Clone, Debug)]
559pub struct ShapeLine {
560 pub color: String,
561 pub width: u32, }
563
564impl ShapeLine {
565 pub fn new(color: &str, width: u32) -> Self {
567 ShapeLine {
568 color: color.trim_start_matches('#').to_uppercase(),
569 width,
570 }
571 }
572}
573
574use crate::core::{Positioned, ElementSized, Dimension};
575
576#[derive(Clone, Debug)]
578pub struct Shape {
579 pub shape_type: ShapeType,
580 pub x: u32, pub y: u32, pub width: u32, pub height: u32, pub fill: Option<ShapeFill>,
585 pub gradient: Option<GradientFill>,
586 pub line: Option<ShapeLine>,
587 pub text: Option<String>,
588 pub id: Option<u32>,
590 pub rotation: Option<i32>,
592 pub hyperlink: Option<crate::generator::hyperlinks::Hyperlink>,
594}
595
596impl Shape {
597 pub fn new(shape_type: ShapeType, x: u32, y: u32, width: u32, height: u32) -> Self {
599 Shape {
600 shape_type,
601 x,
602 y,
603 width,
604 height,
605 fill: None,
606 gradient: None,
607 line: None,
608 text: None,
609 id: None,
610 rotation: None,
611 hyperlink: None,
612 }
613 }
614
615 pub fn with_id(mut self, id: u32) -> Self {
617 self.id = Some(id);
618 self
619 }
620
621 pub fn with_rotation(mut self, degrees: i32) -> Self {
623 self.rotation = Some(degrees);
624 self
625 }
626
627 pub fn with_hyperlink(mut self, hyperlink: crate::generator::hyperlinks::Hyperlink) -> Self {
629 self.hyperlink = Some(hyperlink);
630 self
631 }
632
633 pub fn with_fill(mut self, fill: ShapeFill) -> Self {
635 self.fill = Some(fill);
636 self.gradient = None; self
638 }
639
640 pub fn with_gradient(mut self, gradient: GradientFill) -> Self {
642 self.gradient = Some(gradient);
643 self.fill = None; self
645 }
646
647 pub fn with_line(mut self, line: ShapeLine) -> Self {
649 self.line = Some(line);
650 self
651 }
652
653 pub fn with_text(mut self, text: &str) -> Self {
655 self.text = Some(text.to_string());
656 self
657 }
658
659 pub fn from_dimensions(shape_type: ShapeType, x: Dimension, y: Dimension, width: Dimension, height: Dimension) -> Self {
673 Shape::new(shape_type, x.to_emu_x(), y.to_emu_y(), width.to_emu_x(), height.to_emu_y())
674 }
675
676 pub fn at(mut self, x: Dimension, y: Dimension) -> Self {
686 self.x = x.to_emu_x();
687 self.y = y.to_emu_y();
688 self
689 }
690
691 pub fn with_dimensions(mut self, width: Dimension, height: Dimension) -> Self {
701 self.width = width.to_emu_x();
702 self.height = height.to_emu_y();
703 self
704 }
705}
706
707impl Positioned for Shape {
708 fn x(&self) -> u32 { self.x }
709 fn y(&self) -> u32 { self.y }
710 fn set_position(&mut self, x: u32, y: u32) {
711 self.x = x;
712 self.y = y;
713 }
714}
715
716impl ElementSized for Shape {
717 fn width(&self) -> u32 { self.width }
718 fn height(&self) -> u32 { self.height }
719 fn set_size(&mut self, width: u32, height: u32) {
720 self.width = width;
721 self.height = height;
722 }
723}
724
725pub fn emu_to_inches(emu: u32) -> f64 {
727 emu as f64 / 914400.0
728}
729
730pub fn inches_to_emu(inches: f64) -> u32 {
732 (inches * 914400.0) as u32
733}
734
735pub fn cm_to_emu(cm: f64) -> u32 {
737 (cm * 360000.0) as u32
738}
739
740#[cfg(test)]
741mod tests {
742 use super::*;
743
744 #[test]
745 fn test_shape_type_names() {
746 assert_eq!(ShapeType::Rectangle.preset_name(), "rect");
747 assert_eq!(ShapeType::Circle.preset_name(), "ellipse");
748 assert_eq!(ShapeType::RightArrow.preset_name(), "rightArrow");
749 assert_eq!(ShapeType::Star5.preset_name(), "star5");
750 assert_eq!(ShapeType::Heart.preset_name(), "heart");
751 }
752
753 #[test]
754 fn test_shape_fill_builder() {
755 let fill = ShapeFill::new("FF0000").transparency(50);
756 assert_eq!(fill.color, "FF0000");
757 assert_eq!(fill.transparency, Some(50000));
758 }
759
760 #[test]
761 fn test_shape_builder() {
762 let shape = Shape::new(ShapeType::Rectangle, 0, 0, 1000000, 500000)
763 .with_fill(ShapeFill::new("0000FF"))
764 .with_line(ShapeLine::new("000000", 25400))
765 .with_text("Hello");
766
767 assert_eq!(shape.x, 0);
768 assert_eq!(shape.width, 1000000);
769 assert_eq!(shape.text, Some("Hello".to_string()));
770 }
771
772 #[test]
773 fn test_emu_conversions() {
774 let emu = inches_to_emu(1.0);
775 assert_eq!(emu, 914400);
776 assert!((emu_to_inches(emu) - 1.0).abs() < 0.001);
777 }
778
779 #[test]
780 fn test_cm_to_emu() {
781 let emu = cm_to_emu(2.54); assert_eq!(emu, 914400);
783 }
784
785 #[test]
786 fn test_shape_from_dimensions_ratio() {
787 let shape = Shape::from_dimensions(
788 ShapeType::Rectangle,
789 Dimension::Ratio(0.1), Dimension::Ratio(0.2),
790 Dimension::Ratio(0.8), Dimension::Ratio(0.6),
791 );
792 assert_eq!(shape.x, 914400);
794 assert_eq!(shape.y, 1371600);
796 assert_eq!(shape.width, 7315200);
798 assert_eq!(shape.height, 4114800);
800 }
801
802 #[test]
803 fn test_shape_from_dimensions_mixed() {
804 let shape = Shape::from_dimensions(
805 ShapeType::Ellipse,
806 Dimension::Inches(1.0), Dimension::Cm(2.54),
807 Dimension::Pt(72.0), Dimension::Ratio(0.5),
808 );
809 assert_eq!(shape.x, 914400); assert_eq!(shape.y, 914400); assert_eq!(shape.width, 914400); assert_eq!(shape.height, 6858000 / 2); }
814
815 #[test]
816 fn test_shape_at_fluent() {
817 let shape = Shape::new(ShapeType::Rectangle, 0, 0, 1000000, 500000)
818 .at(Dimension::Ratio(0.5), Dimension::Inches(2.0));
819 assert_eq!(shape.x, 9144000 / 2);
820 assert_eq!(shape.y, 914400 * 2);
821 assert_eq!(shape.width, 1000000); }
823
824 #[test]
825 fn test_shape_with_dimensions_fluent() {
826 let shape = Shape::new(ShapeType::Rectangle, 100, 200, 0, 0)
827 .with_dimensions(Dimension::Ratio(0.9), Dimension::Cm(5.0));
828 assert_eq!(shape.x, 100); assert_eq!(shape.y, 200); assert_eq!(shape.width, (9144000.0 * 0.9) as u32);
831 assert_eq!(shape.height, (5.0 * 360000.0) as u32);
832 }
833
834 #[test]
835 fn test_shape_chained_at_and_dimensions() {
836 let shape = Shape::new(ShapeType::Star5, 0, 0, 0, 0)
837 .at(Dimension::percent(10.0), Dimension::percent(20.0))
838 .with_dimensions(Dimension::percent(80.0), Dimension::percent(60.0))
839 .with_fill(ShapeFill::new("FF0000"));
840 assert_eq!(shape.x, 914400);
841 assert_eq!(shape.y, 1371600);
842 assert_eq!(shape.width, 7315200);
843 assert_eq!(shape.height, 4114800);
844 assert!(shape.fill.is_some());
845 }
846}