Skip to main content

hwpforge_foundation/enums/
border_fill.rs

1//! Border and fill enums: line types, brush/gradient/pattern fills, and image fill modes.
2
3use crate::error::FoundationError;
4use serde::{Deserialize, Serialize};
5use std::fmt;
6
7// ---------------------------------------------------------------------------
8// BorderLineType
9// ---------------------------------------------------------------------------
10
11/// Border line type.
12///
13/// # Examples
14///
15/// ```
16/// use hwpforge_foundation::BorderLineType;
17///
18/// assert_eq!(BorderLineType::default(), BorderLineType::None);
19/// ```
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
21#[non_exhaustive]
22#[repr(u8)]
23pub enum BorderLineType {
24    /// No border.
25    #[default]
26    None = 0,
27    /// Solid line.
28    Solid = 1,
29    /// Dashed line.
30    Dash = 2,
31    /// Dotted line.
32    Dot = 3,
33    /// Dash-dot pattern.
34    DashDot = 4,
35    /// Dash-dot-dot pattern.
36    DashDotDot = 5,
37    /// Long dash pattern.
38    LongDash = 6,
39    /// Triple dot pattern.
40    TripleDot = 7,
41    /// Double line.
42    Double = 8,
43    /// Thin-thick double.
44    DoubleSlim = 9,
45    /// Thick-thin double.
46    ThickBetweenSlim = 10,
47}
48
49impl fmt::Display for BorderLineType {
50    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51        match self {
52            Self::None => f.write_str("None"),
53            Self::Solid => f.write_str("Solid"),
54            Self::Dash => f.write_str("Dash"),
55            Self::Dot => f.write_str("Dot"),
56            Self::DashDot => f.write_str("DashDot"),
57            Self::DashDotDot => f.write_str("DashDotDot"),
58            Self::LongDash => f.write_str("LongDash"),
59            Self::TripleDot => f.write_str("TripleDot"),
60            Self::Double => f.write_str("Double"),
61            Self::DoubleSlim => f.write_str("DoubleSlim"),
62            Self::ThickBetweenSlim => f.write_str("ThickBetweenSlim"),
63        }
64    }
65}
66
67impl std::str::FromStr for BorderLineType {
68    type Err = FoundationError;
69
70    fn from_str(s: &str) -> Result<Self, Self::Err> {
71        match s {
72            "None" | "none" => Ok(Self::None),
73            "Solid" | "solid" => Ok(Self::Solid),
74            "Dash" | "dash" => Ok(Self::Dash),
75            "Dot" | "dot" => Ok(Self::Dot),
76            "DashDot" | "dashdot" | "dash_dot" => Ok(Self::DashDot),
77            "DashDotDot" | "dashdotdot" | "dash_dot_dot" => Ok(Self::DashDotDot),
78            "LongDash" | "longdash" | "long_dash" => Ok(Self::LongDash),
79            "TripleDot" | "tripledot" | "triple_dot" => Ok(Self::TripleDot),
80            "Double" | "double" => Ok(Self::Double),
81            "DoubleSlim" | "doubleslim" | "double_slim" => Ok(Self::DoubleSlim),
82            "ThickBetweenSlim" | "thickbetweenslim" | "thick_between_slim" => {
83                Ok(Self::ThickBetweenSlim)
84            }
85            _ => Err(FoundationError::ParseError {
86                type_name: "BorderLineType".to_string(),
87                value: s.to_string(),
88                valid_values: "None, Solid, Dash, Dot, DashDot, DashDotDot, LongDash, TripleDot, Double, DoubleSlim, ThickBetweenSlim".to_string(),
89            }),
90        }
91    }
92}
93
94impl TryFrom<u8> for BorderLineType {
95    type Error = FoundationError;
96
97    fn try_from(value: u8) -> Result<Self, Self::Error> {
98        match value {
99            0 => Ok(Self::None),
100            1 => Ok(Self::Solid),
101            2 => Ok(Self::Dash),
102            3 => Ok(Self::Dot),
103            4 => Ok(Self::DashDot),
104            5 => Ok(Self::DashDotDot),
105            6 => Ok(Self::LongDash),
106            7 => Ok(Self::TripleDot),
107            8 => Ok(Self::Double),
108            9 => Ok(Self::DoubleSlim),
109            10 => Ok(Self::ThickBetweenSlim),
110            _ => Err(FoundationError::ParseError {
111                type_name: "BorderLineType".to_string(),
112                value: value.to_string(),
113                valid_values: "0-10 (None, Solid, Dash, Dot, DashDot, DashDotDot, LongDash, TripleDot, Double, DoubleSlim, ThickBetweenSlim)".to_string(),
114            }),
115        }
116    }
117}
118
119impl schemars::JsonSchema for BorderLineType {
120    fn schema_name() -> std::borrow::Cow<'static, str> {
121        std::borrow::Cow::Borrowed("BorderLineType")
122    }
123
124    fn json_schema(gen: &mut schemars::SchemaGenerator) -> schemars::Schema {
125        gen.subschema_for::<String>()
126    }
127}
128
129// ---------------------------------------------------------------------------
130// TextBorderType
131// ---------------------------------------------------------------------------
132
133/// Reference frame for page border offset measurement.
134///
135/// Controls whether page border offsets are measured from the paper edge
136/// or from the content area.
137///
138/// # Examples
139///
140/// ```
141/// use hwpforge_foundation::TextBorderType;
142///
143/// assert_eq!(TextBorderType::default(), TextBorderType::Paper);
144/// assert_eq!(TextBorderType::Paper.to_string(), "Paper");
145/// ```
146#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
147#[non_exhaustive]
148#[repr(u8)]
149pub enum TextBorderType {
150    /// Offsets measured from paper edge (default).
151    #[default]
152    Paper = 0,
153    /// Offsets measured from content area.
154    Content = 1,
155}
156
157impl fmt::Display for TextBorderType {
158    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159        match self {
160            Self::Paper => f.write_str("Paper"),
161            Self::Content => f.write_str("Content"),
162        }
163    }
164}
165
166impl std::str::FromStr for TextBorderType {
167    type Err = FoundationError;
168
169    fn from_str(s: &str) -> Result<Self, Self::Err> {
170        match s {
171            "Paper" | "PAPER" | "paper" => Ok(Self::Paper),
172            "Content" | "CONTENT" | "content" => Ok(Self::Content),
173            _ => Err(FoundationError::ParseError {
174                type_name: "TextBorderType".to_string(),
175                value: s.to_string(),
176                valid_values: "Paper, Content".to_string(),
177            }),
178        }
179    }
180}
181
182impl TryFrom<u8> for TextBorderType {
183    type Error = FoundationError;
184
185    fn try_from(value: u8) -> Result<Self, Self::Error> {
186        match value {
187            0 => Ok(Self::Paper),
188            1 => Ok(Self::Content),
189            _ => Err(FoundationError::ParseError {
190                type_name: "TextBorderType".to_string(),
191                value: value.to_string(),
192                valid_values: "0 (Paper), 1 (Content)".to_string(),
193            }),
194        }
195    }
196}
197
198impl schemars::JsonSchema for TextBorderType {
199    fn schema_name() -> std::borrow::Cow<'static, str> {
200        std::borrow::Cow::Borrowed("TextBorderType")
201    }
202
203    fn json_schema(gen: &mut schemars::SchemaGenerator) -> schemars::Schema {
204        gen.subschema_for::<String>()
205    }
206}
207
208// ---------------------------------------------------------------------------
209// FillBrushType
210// ---------------------------------------------------------------------------
211
212/// Fill brush type for backgrounds.
213///
214/// # Examples
215///
216/// ```
217/// use hwpforge_foundation::FillBrushType;
218///
219/// assert_eq!(FillBrushType::default(), FillBrushType::None);
220/// ```
221#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
222#[non_exhaustive]
223#[repr(u8)]
224pub enum FillBrushType {
225    /// No fill (transparent, default).
226    #[default]
227    None = 0,
228    /// Solid color fill.
229    Solid = 1,
230    /// Gradient fill (linear or radial).
231    Gradient = 2,
232    /// Pattern fill (hatch, dots, etc.).
233    Pattern = 3,
234}
235
236impl fmt::Display for FillBrushType {
237    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
238        match self {
239            Self::None => f.write_str("None"),
240            Self::Solid => f.write_str("Solid"),
241            Self::Gradient => f.write_str("Gradient"),
242            Self::Pattern => f.write_str("Pattern"),
243        }
244    }
245}
246
247impl std::str::FromStr for FillBrushType {
248    type Err = FoundationError;
249
250    fn from_str(s: &str) -> Result<Self, Self::Err> {
251        match s {
252            "None" | "none" => Ok(Self::None),
253            "Solid" | "solid" => Ok(Self::Solid),
254            "Gradient" | "gradient" => Ok(Self::Gradient),
255            "Pattern" | "pattern" => Ok(Self::Pattern),
256            _ => Err(FoundationError::ParseError {
257                type_name: "FillBrushType".to_string(),
258                value: s.to_string(),
259                valid_values: "None, Solid, Gradient, Pattern".to_string(),
260            }),
261        }
262    }
263}
264
265impl TryFrom<u8> for FillBrushType {
266    type Error = FoundationError;
267
268    fn try_from(value: u8) -> Result<Self, Self::Error> {
269        match value {
270            0 => Ok(Self::None),
271            1 => Ok(Self::Solid),
272            2 => Ok(Self::Gradient),
273            3 => Ok(Self::Pattern),
274            _ => Err(FoundationError::ParseError {
275                type_name: "FillBrushType".to_string(),
276                value: value.to_string(),
277                valid_values: "0 (None), 1 (Solid), 2 (Gradient), 3 (Pattern)".to_string(),
278            }),
279        }
280    }
281}
282
283impl schemars::JsonSchema for FillBrushType {
284    fn schema_name() -> std::borrow::Cow<'static, str> {
285        std::borrow::Cow::Borrowed("FillBrushType")
286    }
287
288    fn json_schema(gen: &mut schemars::SchemaGenerator) -> schemars::Schema {
289        gen.subschema_for::<String>()
290    }
291}
292
293// ---------------------------------------------------------------------------
294// GradientType
295// ---------------------------------------------------------------------------
296
297/// Gradient fill direction type.
298///
299/// # Examples
300///
301/// ```
302/// use hwpforge_foundation::GradientType;
303///
304/// assert_eq!(GradientType::default(), GradientType::Linear);
305/// ```
306#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
307#[non_exhaustive]
308#[repr(u8)]
309pub enum GradientType {
310    /// Linear gradient (default).
311    #[default]
312    Linear = 0,
313    /// Radial gradient (from center outward).
314    Radial = 1,
315    /// Square/rectangular gradient.
316    Square = 2,
317    /// Conical gradient (angular sweep).
318    Conical = 3,
319}
320
321impl fmt::Display for GradientType {
322    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
323        match self {
324            Self::Linear => f.write_str("LINEAR"),
325            Self::Radial => f.write_str("RADIAL"),
326            Self::Square => f.write_str("SQUARE"),
327            Self::Conical => f.write_str("CONICAL"),
328        }
329    }
330}
331
332impl std::str::FromStr for GradientType {
333    type Err = FoundationError;
334
335    fn from_str(s: &str) -> Result<Self, Self::Err> {
336        match s {
337            "LINEAR" | "Linear" | "linear" => Ok(Self::Linear),
338            "RADIAL" | "Radial" | "radial" => Ok(Self::Radial),
339            "SQUARE" | "Square" | "square" => Ok(Self::Square),
340            "CONICAL" | "Conical" | "conical" => Ok(Self::Conical),
341            _ => Err(FoundationError::ParseError {
342                type_name: "GradientType".to_string(),
343                value: s.to_string(),
344                valid_values: "LINEAR, RADIAL, SQUARE, CONICAL".to_string(),
345            }),
346        }
347    }
348}
349
350impl TryFrom<u8> for GradientType {
351    type Error = FoundationError;
352
353    fn try_from(value: u8) -> Result<Self, Self::Error> {
354        match value {
355            0 => Ok(Self::Linear),
356            1 => Ok(Self::Radial),
357            2 => Ok(Self::Square),
358            3 => Ok(Self::Conical),
359            _ => Err(FoundationError::ParseError {
360                type_name: "GradientType".to_string(),
361                value: value.to_string(),
362                valid_values: "0 (Linear), 1 (Radial), 2 (Square), 3 (Conical)".to_string(),
363            }),
364        }
365    }
366}
367
368impl schemars::JsonSchema for GradientType {
369    fn schema_name() -> std::borrow::Cow<'static, str> {
370        std::borrow::Cow::Borrowed("GradientType")
371    }
372
373    fn json_schema(gen: &mut schemars::SchemaGenerator) -> schemars::Schema {
374        gen.subschema_for::<String>()
375    }
376}
377
378// ---------------------------------------------------------------------------
379// PatternType
380// ---------------------------------------------------------------------------
381
382/// Hatch/pattern fill type for shapes.
383///
384/// # Examples
385///
386/// ```
387/// use hwpforge_foundation::PatternType;
388///
389/// assert_eq!(PatternType::default(), PatternType::Horizontal);
390/// ```
391#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
392#[non_exhaustive]
393#[repr(u8)]
394pub enum PatternType {
395    /// Horizontal lines (default).
396    #[default]
397    Horizontal = 0,
398    /// Vertical lines.
399    Vertical = 1,
400    /// Backslash diagonal lines.
401    BackSlash = 2,
402    /// Forward slash diagonal lines.
403    Slash = 3,
404    /// Cross-hatch (horizontal + vertical).
405    Cross = 4,
406    /// Cross-diagonal hatch.
407    CrossDiagonal = 5,
408}
409
410impl fmt::Display for PatternType {
411    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
412        // 한글 renders BACK_SLASH as `/` and SLASH as `\` — opposite to KS X 6101 XSD docs.
413        // We swap the mapping so our semantic enum values match actual rendering.
414        match self {
415            Self::Horizontal => f.write_str("HORIZONTAL"),
416            Self::Vertical => f.write_str("VERTICAL"),
417            Self::BackSlash => f.write_str("SLASH"),
418            Self::Slash => f.write_str("BACK_SLASH"),
419            Self::Cross => f.write_str("CROSS"),
420            Self::CrossDiagonal => f.write_str("CROSS_DIAGONAL"),
421        }
422    }
423}
424
425impl std::str::FromStr for PatternType {
426    type Err = FoundationError;
427
428    fn from_str(s: &str) -> Result<Self, Self::Err> {
429        // Swapped BACK_SLASH/SLASH to match Display (한글 renders them opposite to spec).
430        // Only SCREAMING_CASE forms used here — PascalCase comes through serde derive.
431        match s {
432            "HORIZONTAL" | "horizontal" => Ok(Self::Horizontal),
433            "VERTICAL" | "vertical" => Ok(Self::Vertical),
434            "BACK_SLASH" | "backslash" => Ok(Self::Slash),
435            "SLASH" | "slash" => Ok(Self::BackSlash),
436            "CROSS" | "cross" => Ok(Self::Cross),
437            "CROSS_DIAGONAL" | "crossdiagonal" => Ok(Self::CrossDiagonal),
438            _ => Err(FoundationError::ParseError {
439                type_name: "PatternType".to_string(),
440                value: s.to_string(),
441                valid_values: "HORIZONTAL, VERTICAL, BACK_SLASH, SLASH, CROSS, CROSS_DIAGONAL"
442                    .to_string(),
443            }),
444        }
445    }
446}
447
448impl TryFrom<u8> for PatternType {
449    type Error = FoundationError;
450
451    fn try_from(value: u8) -> Result<Self, Self::Error> {
452        match value {
453            0 => Ok(Self::Horizontal),
454            1 => Ok(Self::Vertical),
455            2 => Ok(Self::BackSlash),
456            3 => Ok(Self::Slash),
457            4 => Ok(Self::Cross),
458            5 => Ok(Self::CrossDiagonal),
459            _ => Err(FoundationError::ParseError {
460                type_name: "PatternType".to_string(),
461                value: value.to_string(),
462                valid_values:
463                    "0 (Horizontal), 1 (Vertical), 2 (BackSlash), 3 (Slash), 4 (Cross), 5 (CrossDiagonal)"
464                        .to_string(),
465            }),
466        }
467    }
468}
469
470impl schemars::JsonSchema for PatternType {
471    fn schema_name() -> std::borrow::Cow<'static, str> {
472        std::borrow::Cow::Borrowed("PatternType")
473    }
474
475    fn json_schema(gen: &mut schemars::SchemaGenerator) -> schemars::Schema {
476        gen.subschema_for::<String>()
477    }
478}
479
480// ---------------------------------------------------------------------------
481// ImageFillMode
482// ---------------------------------------------------------------------------
483
484/// How an image is fitted within a shape fill area.
485///
486/// # Examples
487///
488/// ```
489/// use hwpforge_foundation::ImageFillMode;
490///
491/// assert_eq!(ImageFillMode::default(), ImageFillMode::Tile);
492/// ```
493#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
494#[non_exhaustive]
495#[repr(u8)]
496pub enum ImageFillMode {
497    /// Tile the image to fill the area (default).
498    #[default]
499    Tile = 0,
500    /// Center the image without scaling.
501    Center = 1,
502    /// Stretch the image to fit exactly.
503    Stretch = 2,
504    /// Scale proportionally to fit all within the area.
505    FitAll = 3,
506}
507
508impl fmt::Display for ImageFillMode {
509    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
510        match self {
511            Self::Tile => f.write_str("TILE"),
512            Self::Center => f.write_str("CENTER"),
513            Self::Stretch => f.write_str("STRETCH"),
514            Self::FitAll => f.write_str("FIT_ALL"),
515        }
516    }
517}
518
519impl std::str::FromStr for ImageFillMode {
520    type Err = FoundationError;
521
522    fn from_str(s: &str) -> Result<Self, Self::Err> {
523        match s {
524            "TILE" | "Tile" | "tile" => Ok(Self::Tile),
525            "CENTER" | "Center" | "center" => Ok(Self::Center),
526            "STRETCH" | "Stretch" | "stretch" => Ok(Self::Stretch),
527            "FIT_ALL" | "FitAll" | "fit_all" => Ok(Self::FitAll),
528            _ => Err(FoundationError::ParseError {
529                type_name: "ImageFillMode".to_string(),
530                value: s.to_string(),
531                valid_values: "TILE, CENTER, STRETCH, FIT_ALL".to_string(),
532            }),
533        }
534    }
535}
536
537impl TryFrom<u8> for ImageFillMode {
538    type Error = FoundationError;
539
540    fn try_from(value: u8) -> Result<Self, Self::Error> {
541        match value {
542            0 => Ok(Self::Tile),
543            1 => Ok(Self::Center),
544            2 => Ok(Self::Stretch),
545            3 => Ok(Self::FitAll),
546            _ => Err(FoundationError::ParseError {
547                type_name: "ImageFillMode".to_string(),
548                value: value.to_string(),
549                valid_values: "0 (Tile), 1 (Center), 2 (Stretch), 3 (FitAll)".to_string(),
550            }),
551        }
552    }
553}
554
555impl schemars::JsonSchema for ImageFillMode {
556    fn schema_name() -> std::borrow::Cow<'static, str> {
557        std::borrow::Cow::Borrowed("ImageFillMode")
558    }
559
560    fn json_schema(gen: &mut schemars::SchemaGenerator) -> schemars::Schema {
561        gen.subschema_for::<String>()
562    }
563}