coded_chars/
presentation.rs

1//! This module provides control function to change the presentation.
2
3use std::fmt::{Display, Formatter};
4use crate::control::ControlSequence;
5use crate::escape::{escape, EscapeSequence};
6
7/// # Break permitted here
8///
9/// BPH is used to indicate a point where a line break may occur when text is formatted. BPH may occur
10/// between two graphic characters, either or both of which may be SPACE.
11pub const BPH: EscapeSequence = escape('B');
12
13/// # No break here
14///
15/// NBH is used to indicate a point where a line break shall not occur when text is formatted. NBH may
16/// occur between two graphic characters either or both of which may be SPACE.
17pub const NBH: EscapeSequence = escape('C');
18
19/// # DTA - Dimension text area
20///
21/// DTA is used to establish the dimensions of the text area for subsequent pages.
22/// The established dimensions remain in effect until the next occurrence of DTA in the data stream.
23///  - `l` specifies the dimension in the direction perpendicular to the line orientation.
24///  - `c` specifies the dimension in the direction parallel to the line orientation.
25///
26/// The unit in which the parameter value is expressed is that established by the parameter value of SELECT
27/// SIZE UNIT (SSU).
28pub fn dimension_text(l: usize, c: usize) -> ControlSequence {
29    ControlSequence::new(&[&l.to_string(), &c.to_string()], " T")
30}
31
32/// # FNT - Font selection
33///
34/// FNT is used to identify the character font to be selected as primary or alternative font by subsequent
35/// occurrences of SELECT GRAPHIC RENDITION (SGR) in the data stream.
36pub fn select_font(font: Font) -> ControlSequence {
37    ControlSequence::new(&[&font.to_string(), "0"], " D")
38}
39
40#[derive(Copy, Clone, Debug)]
41pub enum Font {
42    Primary,
43    Alternative1,
44    Alternative2,
45    Alternative3,
46    Alternative4,
47    Alternative5,
48    Alternative6,
49    Alternative7,
50    Alternative8,
51    Alternative9,
52}
53
54impl Display for Font {
55    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
56        write!(f, "{}", match self {
57            Font::Primary => "0",
58            Font::Alternative1 => "1",
59            Font::Alternative2 => "2",
60            Font::Alternative3 => "3",
61            Font::Alternative4 => "4",
62            Font::Alternative5 => "5",
63            Font::Alternative6 => "6",
64            Font::Alternative7 => "7",
65            Font::Alternative8 => "8",
66            Font::Alternative9 => "9"
67        })
68    }
69}
70
71/// # GCC - Graphic character combination
72///
73/// GCC is used to indicate that two or more graphic characters are to be imaged as one single graphic
74/// symbol. GCC with a parameter value of 0 indicates that the following two graphic characters are to be
75/// imaged as one single graphic symbol; GCC with a parameter value of 1 and GCC with a parameter value
76/// of 2 indicate respectively the beginning and the end of a string of graphic characters which are to be
77/// imaged as one single graphic symbol.
78///
79/// ### Note
80/// GCC does not explicitly specify the relative sizes or placements of the component parts of a composite
81/// graphic symbol. In the simplest case, two components may be "half-width" and side-by-side. For
82/// example, in Japanese text a pair of characters may be presented side-by-side, and occupy the space of a
83/// normal-size Kanji character.
84pub fn character_combination(combination: Combination) -> ControlSequence {
85    ControlSequence::new(&[&combination.to_string()], " _")
86}
87
88#[derive(Copy, Clone, Debug)]
89pub enum Combination {
90    Two,
91    Start,
92    End,
93}
94
95impl Display for Combination {
96    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
97        write!(f, "{}", match self {
98            Self::Two => "0",
99            Self::Start => "1",
100            Self::End => "2"
101        })
102    }
103}
104
105/// # GSM - Graphic size modification
106///
107/// GSM is used to modify for subsequent text the height and/or the width of all primary and alternative
108/// fonts identified by FONT SELECTION (FNT) and established by GRAPHIC SIZE SELECTION (GSS).
109///
110/// The established values remain in effect until the next occurrence of GSM or GSS in the data steam.
111///
112/// `height` and `width` are percentage of values established by GSS ([select_size]).
113pub fn modify_size(height: usize, width: usize) -> ControlSequence {
114    ControlSequence::new(&[&height.to_string(), &width.to_string()], " B")
115}
116
117/// # GSS - Graphic size selection
118///
119/// GSS is used to establish for subsequent text the height and the width of all primary and alternative fonts
120/// identified by FONT SELECTION (FNT). The established values remain in effect until the next
121/// occurrence of GSS in the data stream.
122///
123/// `n` specifies the height, the width is implicitly defined by the height.
124///
125/// The unit in which the parameter value is expressed is that established by the parameter value of SELECT
126/// SIZE UNIT (SSU).
127pub fn select_size(n: usize) -> ControlSequence {
128    ControlSequence::new(&[&n.to_string()], " C")
129}
130
131/// # JFY - Justify
132///
133/// JFY is used to indicate the beginning of a string of graphic characters in the presentation component that
134/// are to be justified according to the layout specified by the parameter values.
135///
136/// The end of the string to be justified is indicated by the next occurrence of JFY in the data stream.
137pub fn justify(modes: &[JustifyMode]) -> ControlSequence {
138    let str_modes: Vec<String> = modes.iter()
139        .map(|mode| mode.to_string())
140        .collect();
141
142    let str_ref_modes: Vec<&str> = str_modes.iter()
143        .map(AsRef::as_ref)
144        .collect();
145
146    ControlSequence::new(&str_ref_modes, " F")
147}
148
149#[derive(Copy, Clone, Debug)]
150pub enum JustifyMode {
151    /// No justification, end of justification of preceding text.
152    None,
153    WordFill,
154    WordSpace,
155    LetterSpace,
156    Hyphen,
157    /// Flush to line home position margin.
158    FlushHome,
159    /// Centre between line home position and line limit position margins.
160    Center,
161    /// Flush to line limit position margin.
162    FlushLimit,
163    ItalianHyphen,
164}
165
166impl Display for JustifyMode {
167    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
168        write!(f, "{}", match self {
169            JustifyMode::None => "0",
170            JustifyMode::WordFill => "1",
171            JustifyMode::WordSpace => "2",
172            JustifyMode::LetterSpace => "3",
173            JustifyMode::Hyphen => "4",
174            JustifyMode::FlushHome => "5",
175            JustifyMode::Center => "6",
176            JustifyMode::FlushLimit => "7",
177            JustifyMode::ItalianHyphen => "8",
178        })
179    }
180}
181
182/// # PEC - Presentation expand or contract
183///
184/// PEC is used to establish the spacing and the extent of the graphic characters for subsequent text. The
185/// spacing is specified in the line as multiples of the spacing established by the most recent occurrence of
186/// SET CHARACTER SPACING (SCS) or of SELECT CHARACTER SPACING (SHS) or of SPACING
187/// INCREMENT (SPI) in the data stream. The extent of the characters is implicitly established by these
188/// control functions. The established spacing and the extent remain in effect until the next occurrence of
189/// PEC, of SCS, of SHS or of SPI in the data stream.
190pub fn expand_or_condense(expansion: Expansion) -> ControlSequence {
191    ControlSequence::new(&[&expansion.to_string()], " Z")
192}
193
194#[derive(Copy, Clone, Debug)]
195pub enum Expansion {
196    Normal,
197    Expanded,
198    Condensed,
199}
200
201impl Display for Expansion {
202    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
203        write!(f, "{}", match self {
204            Self::Normal => "0",
205            Self::Expanded => "1",
206            Self::Condensed => "2"
207        })
208    }
209}
210
211/// # PFS - Page format selection
212///
213/// PFS is used to establish the available area for the imaging of pages of text based on paper size. The
214/// pages are introduced by the subsequent occurrence of FORM FEED (FF) in the data stream.
215///
216/// The established image area remains in effect until the next occurrence of PFS in the data stream.
217///
218/// The page home position is established by the parameter value of SET PAGE HOME (SPH), the page
219/// limit position is established by the parameter value of SET PAGE LIMIT (SPL).
220pub fn select_page_format(page_format: PageFormat) -> ControlSequence {
221    ControlSequence::new(&[&page_format.to_string()], " J")
222}
223
224#[derive(Copy, Clone, Debug)]
225pub enum PageFormat {
226    TallText,
227    WideText,
228    TallA4,
229    WideA4,
230    TallLetter,
231    WideLetter,
232    TallExtA4,
233    WideExtA4,
234    TallLegal,
235    WideLegal,
236    A4ShortLines,
237    A4LongLines,
238    B5ShortLines,
239    B5LongLines,
240    B4ShortLines,
241    B4LongLines,
242}
243
244impl Display for PageFormat {
245    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
246        write!(f, "{}", match self {
247            PageFormat::TallText => "0",
248            PageFormat::WideText => "1",
249            PageFormat::TallA4 => "2",
250            PageFormat::WideA4 => "3",
251            PageFormat::TallLetter => "4",
252            PageFormat::WideLetter => "5",
253            PageFormat::TallExtA4 => "6",
254            PageFormat::WideExtA4 => "7",
255            PageFormat::TallLegal => "8",
256            PageFormat::WideLegal => "9",
257            PageFormat::A4ShortLines => "10",
258            PageFormat::A4LongLines => "11",
259            PageFormat::B5ShortLines => "12",
260            PageFormat::B5LongLines => "13",
261            PageFormat::B4ShortLines => "14",
262            PageFormat::B4LongLines => "15",
263        })
264    }
265}
266
267
268/// # PTX - Parallel texts
269///
270/// PTX is used to delimit strings of graphic characters that are communicated one after another in the data
271/// stream but that are intended to be presented in parallel with one another, usually in adjacent lines.
272///
273/// PTX with a parameter value of 1 indicates the beginning of the string of principal text intended to be
274/// presented in parallel with one or more strings of supplementary text.
275///
276/// PTX with a parameter value of 2, 3 or 4 indicates the beginning of a string of supplementary text that is
277/// intended to be presented in parallel with either a string of principal text or the immediately preceding
278/// string of supplementary text, if any; at the same time it indicates the end of the preceding string of
279/// principal text or of the immediately preceding string of supplementary text, if any. The end of a string of
280/// supplementary text is indicated by a subsequent occurrence of PTX with a parameter value other than 1.
281/// PTX with a parameter value of 0 indicates the end of the strings of text intended to be presented in
282/// parallel with one another.
283///
284///### Note
285/// PTX does not explicitly specify the relative placement of the strings of principal and supplementary
286/// parallel texts, or the relative sizes of graphic characters in the strings of parallel text. A string of
287/// supplementary text is normally presented in a line adjacent to the line containing the string of principal
288/// text, or adjacent to the line containing the immediately preceding string of supplementary text, if any.
289/// The first graphic character of the string of principal text and the first graphic character of a string of
290/// supplementary text are normally presented in the same position of their respective lines. However, a
291/// string of supplementary text longer (when presented) than the associated string of principal text may be
292/// centred on that string. In the case of long strings of text, such as paragraphs in different languages, the
293/// strings may be presented in successive lines in parallel columns, with their beginnings aligned with one
294/// another and the shorter of the paragraphs followed by an appropriate amount of "white space".
295///
296/// Japanese phonetic annotation typically consists of a few half-size or smaller Kana characters which
297/// indicate the pronunciation or interpretation of one or more Kanji characters and are presented above
298/// those Kanji characters if the character path is horizontal, or to the right of them if the character path is
299/// vertical.
300///
301/// Chinese phonetic annotation typically consists of a few Pinyin characters which indicate the
302/// pronunciation of one or more Hanzi characters and are presented above those Hanzi characters.
303/// Alternatively, the Pinyin characters may be presented in the same line as the Hanzi characters and
304/// following the respective Hanzi characters. The Pinyin characters will then be presented within enclosing
305/// pairs of parentheses
306pub fn parallel_texts(text_delimiter: TextDelimiter) -> ControlSequence {
307    ControlSequence::new(&[&text_delimiter.to_string()], "\\")
308}
309
310#[derive(Copy, Clone, Debug)]
311pub enum TextDelimiter {
312    End,
313    BeginPrincipal,
314    BeginSupplementary,
315    BeginSupplementaryPhoneticJapanese,
316    BeginSupplementaryPhoneticChinese,
317    EndPhonetic,
318}
319
320impl Display for TextDelimiter {
321    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
322        write!(f, "{}", match self {
323            TextDelimiter::End => "0",
324            TextDelimiter::BeginPrincipal => "1",
325            TextDelimiter::BeginSupplementary => "2",
326            TextDelimiter::BeginSupplementaryPhoneticJapanese => "3",
327            TextDelimiter::BeginSupplementaryPhoneticChinese => "4",
328            TextDelimiter::EndPhonetic => "5",
329        })
330    }
331}
332
333/// # QUAD
334///
335/// QUAD is used to indicate the end of a string of graphic characters that are to be positioned on a single
336/// line according to the layout specified.
337///
338/// The beginning of the string to be positioned is indicated by the preceding occurrence in the data stream
339/// of either QUAD or one of the following formator functions: FORM FEED (FF), CHARACTER AND
340/// LINE POSITION (HVP), LINE FEED (LF), NEXT LINE (NEL), PAGE POSITION ABSOLUTE (PPA),
341/// PAGE POSITION BACKWARD (PPB), PAGE POSITION FORWARD (PPR), REVERSE LINE FEED
342/// (RI), LINE POSITION ABSOLUTE (VPA), LINE POSITION BACKWARD (VPB), LINE POSITION
343/// FORWARD (VPR), or LINE TABULATION (VT).
344///
345/// The line home position is established by the parameter value of SET LINE HOME (SLH). The line limit
346/// position is established by the parameter value of SET LINE LIMIT (SLL).
347pub fn quad(layouts: &[Layout]) -> ControlSequence {
348    let str_layouts: Vec<String> = layouts.iter()
349        .map(|mode| mode.to_string())
350        .collect();
351
352    let str_ref_modes: Vec<&str> = str_layouts.iter()
353        .map(AsRef::as_ref)
354        .collect();
355
356    ControlSequence::new(&str_ref_modes, " H")
357}
358
359#[derive(Copy, Clone, Debug)]
360pub enum Layout {
361    FlushHome,
362    FlushHomeAndFill,
363    Center,
364    CenterAndFill,
365    FlushLimit,
366    FlushLimitAndFill,
367    FlushBoth,
368}
369
370impl Display for Layout {
371    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
372        write!(f, "{}", match self {
373            Layout::FlushHome => "0",
374            Layout::FlushHomeAndFill => "1",
375            Layout::Center => "2",
376            Layout::CenterAndFill => "3",
377            Layout::FlushLimit => "4",
378            Layout::FlushLimitAndFill => "5",
379            Layout::FlushBoth => "6",
380        })
381    }
382}
383
384/// # REP - Repeat
385///
386/// REP is used to indicate that the preceding character in the data stream, if it is a graphic character
387/// (represented by one or more bit combinations) including SPACE, is to be repeated `n` times.
388///
389/// If the character preceding REP is a control function or part of a control function,
390/// the effect of REP is not defined by this Standard.
391pub fn repeat(n: usize) -> ControlSequence {
392    ControlSequence::new(&[&n.to_string()], "b")
393}
394
395/// # SACS - Set additional character separation
396///
397/// SACS is used to establish extra inter-character escapement for subsequent text. The established extra
398/// escapement remains in effect until the next occurrence of SACS or of SET REDUCED CHARACTER
399/// SEPARATION (SRCS) in the data stream or until it is reset to the default value by a subsequent
400/// occurrence of CARRIAGE RETURN/LINE FEED (CR LF) or of NEXT LINE (NEL) in the data stream.
401///
402/// `n` specifies the number of units by which the inter-character escapement is enlarged.
403///
404/// The unit in which the parameter value is expressed is that established by the parameter value of SELECT
405/// SIZE UNIT (SSU).
406pub fn add_separation(n: usize) -> ControlSequence {
407    ControlSequence::new(&[&n.to_string()], " \\")
408}
409
410/// # SAPV - Select alternative presentation variants
411///
412/// SAPV is used to specify one or more variants for the presentation of subsequent text.
413pub fn select_alternative() -> PresentationVariant {
414    PresentationVariant::new()
415}
416
417#[derive(Clone)]
418pub struct PresentationVariant {
419    modes: Vec<String>,
420}
421impl PresentationVariant {
422    pub fn new() -> Self { Self { modes: vec![] } }
423
424    /// Default presentation (implementation-defined); cancels the effect of any preceding occurrence of
425    /// SAPV in the data stream.
426    pub fn default(&mut self) -> &mut Self { self.add("0") }
427
428    /// The decimal digits are presented by means of the graphic symbols used in the Latin script.
429    pub fn latin_decimal(&mut self) -> &mut Self { self.add("1") }
430
431    /// The decimal digits are presented by means of the graphic symbols used in the Arabic script, i.e. the Hindi symbols.
432    pub fn arabic_decimal(&mut self) -> &mut Self { self.add("2") }
433
434    /// When the direction of the character path is right-to-left, each of the graphic characters in the graphic
435    /// character set(s) in use which is one of a left/right-handed pair (parentheses, square brackets, curly
436    /// brackets, greater-than/less-than signs, etc.) is presented as "mirrored", i.e. as the other member of the
437    /// pair. For example, the coded graphic character given the name LEFT PARENTHESIS is presented as
438    /// RIGHT PARENTHESIS, and vice versa.
439    pub fn mirror_horizontal(&mut self) -> &mut Self { self.add("3") }
440
441    /// When the direction of the character path is right-to-left, all graphic characters which represent
442    /// operators and delimiters in mathematical formulae and which are not symmetrical about a vertical
443    /// axis are presented as mirrored about that vertical axis.
444    pub fn mirror_vertical(&mut self) -> &mut Self { self.add("4") }
445
446    /// The following graphic character is presented in its isolated form.
447    pub fn character_isolate(&mut self) -> &mut Self { self.add("5") }
448
449    /// The following graphic character is presented in its initial form.
450    pub fn character_initial(&mut self) -> &mut Self { self.add("6") }
451
452    /// The following graphic character is presented in its medial form.
453    pub fn character_medial(&mut self) -> &mut Self { self.add("7") }
454
455    /// The following graphic character is presented in its final form.
456    pub fn character_final(&mut self) -> &mut Self { self.add("8") }
457
458    /// Where the bit combination 0x2E is intended to represent a decimal mark in a decimal number it shall
459    /// be presented by means of the graphic symbol FULL STOP.
460    pub fn decimal_stop(&mut self) -> &mut Self { self.add("9") }
461
462    /// Where the bit combination 0x2E is intended to represent a decimal mark in a decimal number it shall
463    /// be presented by means of the graphic symbol COMMA.
464    pub fn decimal_comma(&mut self) -> &mut Self { self.add("10") }
465
466    /// Vowels are presented above or below the preceding character.
467    pub fn vowel_above_or_below(&mut self) -> &mut Self { self.add("11") }
468
469    /// Vowels are presented after the preceding character.
470    pub fn vowel_after(&mut self) -> &mut Self { self.add("12") }
471
472    /// Contextual shape determination of Arabic scripts, including the LAM-ALEPH ligature but excluding
473    /// all other Arabic ligatures.
474    pub fn arabic_ligature_aleph(&mut self) -> &mut Self { self.add("13") }
475
476    /// Contextual shape determination of Arabic scripts, excluding all Arabic ligatures.
477    pub fn arabic_ligature_none(&mut self) -> &mut Self { self.add("14") }
478
479    /// Cancels the effect of parameter values [Self::mirror_horizontal] and [Self::mirror_vertical].
480    pub fn no_mirror(&mut self) -> &mut Self { self.add("15") }
481
482    /// Vowels are not presented.
483    pub fn no_vowel(&mut self) -> &mut Self { self.add("16") }
484
485    /// When the string direction is right-to-left, the italicized characters are slanted to the left; when the
486    /// string direction is left-to-right, the italicized characters are slanted to the right.
487    pub fn italic_direction(&mut self) -> &mut Self { self.add("17") }
488
489    /// Contextual shape determination of Arabic scripts is not used, the graphic characters - including the
490    /// digits - are presented in the form they are stored (Pass-through).
491    pub fn arabic_no_context_with_digit(&mut self) -> &mut Self { self.add("18") }
492
493    /// Contextual shape determination of Arabic scripts is not used, the graphic characters - excluding the
494    /// digits - are presented in the form they are stored (Pass-through).
495    pub fn arabic_no_context(&mut self) -> &mut Self { self.add("19") }
496
497    /// The graphic symbols used to present the decimal digits are device dependent.
498    pub fn device_digit(&mut self) -> &mut Self { self.add("20") }
499
500    /// Establishes the effect of parameter values [Self::character_isolate], [Self::character_initial],
501    /// [Self::character_medial], and [Self::character_final] for the following graphic characters until
502    /// cancelled.
503    pub fn character_establish(&mut self) -> &mut Self { self.add("21") }
504
505    /// Cancels the effect of parameter value [Self::character_establish], i.e. re-establishes the effect
506    /// of parameter values [Self::character_isolate], [Self::character_initial],
507    /// [Self::character_medial], and [Self::character_final] for the next single graphic character only.
508    pub fn character_cancel(&mut self) -> &mut Self { self.add("22") }
509
510    pub fn get(&self) -> ControlSequence {
511        ControlSequence::new(&self.modes.iter().map(|s| s.as_str()).collect::<Vec<_>>(), " ]")
512    }
513    fn add(&mut self, s: &str) -> &mut Self {
514        self.modes.push(s.to_string());
515        self
516    }
517}
518impl Display for PresentationVariant {
519    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
520        write!(f, "{}", self.get())
521    }
522}
523
524/// # SCO - Select character orientation
525///
526/// SCO is used to establish the amount of rotation of the graphic characters following in the data stream.
527/// The established value remains in effect until the next occurrence of SCO in the data stream.
528///
529///
530pub fn character_orientation(orientation: Orientation) -> ControlSequence {
531    ControlSequence::new(&[&orientation.to_string()], " e")
532}
533
534#[derive(Copy, Clone, Debug)]
535pub enum Orientation {
536    /// 0°
537    North,
538    /// 45°
539    NorthWest,
540    /// 90°
541    West,
542    /// 135°
543    SouthWest,
544    /// 180°
545    South,
546    /// 225°
547    SouthEast,
548    /// 270°
549    East,
550    /// 315°
551    NorthEast,
552}
553
554impl Display for Orientation {
555    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
556        write!(f, "{}", match self {
557            Orientation::North => "0",
558            Orientation::NorthWest => "1",
559            Orientation::West => "2",
560            Orientation::SouthWest => "3",
561            Orientation::South => "4",
562            Orientation::SouthEast => "5",
563            Orientation::East => "6",
564            Orientation::NorthEast => "7",
565        })
566    }
567}
568
569/// # SCP - Select character path
570///
571/// SCP is used to select the character path, relative to the line orientation, for the active line (the line that
572/// contains the active presentation position) and subsequent lines in the presentation component. It is also
573/// used to update the content of the active line in the presentation component and the content of the active
574/// line (the line that contains the active data position) in the data component. This takes effect immediately.
575pub fn character_path(character_path: CharacterPath, path_effect: PathEffect) -> ControlSequence {
576    ControlSequence::new(&[&character_path.to_string(), &path_effect.to_string()], " k")
577}
578
579#[derive(Copy, Clone, Debug)]
580pub enum CharacterPath {
581    /// left-to-right (in the case of horizontal line orientation), or top-to-bottom (in the case of vertical line
582    /// orientation).
583    LeftToRight,
584
585    /// right-to-left (in the case of horizontal line orientation), or bottom-to-top (in the case of vertical line
586    /// orientation).
587    RightToLeft,
588}
589
590impl Display for CharacterPath {
591    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
592        write!(f, "{}", match self {
593            CharacterPath::LeftToRight => "1",
594            CharacterPath::RightToLeft => "2",
595        })
596    }
597}
598
599#[derive(Copy, Clone, Debug)]
600pub enum PathEffect {
601    /// Implementation dependant.
602    Undefined,
603
604    /// The content of the active line in the presentation component (the line that contains the active
605    /// presentation position) is updated to correspond to the content of the active line in the data component
606    /// (the line that contains the active data position) according to the newly established character path
607    /// characteristics in the presentation component; the active data position is moved to the first character
608    /// position in the active line in the data component, the active presentation position in the presentation
609    /// component is updated accordingly.
610    UpdatePresentation,
611
612    /// The content of the active line in the data component (the line that contains the active data position) is
613    /// updated to correspond to the content of the active line in the presentation component (the line that
614    /// contains the active presentation position) according to the newly established character path
615    /// characteristics of the presentation component; the active presentation position is moved to the first
616    /// character position in the active line in the presentation component, the active data position in the data
617    /// component is updated accordingly.
618    UpdateData,
619}
620
621impl Display for PathEffect {
622    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
623        write!(f, "{}", match self {
624            PathEffect::Undefined => "0",
625            PathEffect::UpdatePresentation => "1",
626            PathEffect::UpdateData => "2",
627        })
628    }
629}
630
631/// # SDS - Start directed string
632///
633/// SDS is used to establish in the data component the beginning and the end of a string of characters as
634/// well as the direction of the string. This direction may be different from that currently established. The
635/// indicated string follows the preceding text. The established character progression is not affected.
636///
637/// The beginning of a directed string is indicated by SDS with a parameter value not equal to 0. A directed
638/// string may contain one or more nested strings. These nested strings may be directed strings the
639/// beginnings of which are indicated by SDS with a parameter value not equal to 0, or reversed strings the
640/// beginnings of which are indicated by START REVERSED STRING (SRS) with a parameter value of 1.
641///
642/// Every beginning of such a string invokes the next deeper level of nesting.
643///
644/// This Standard does not define the location of the active data position within any such nested string.
645///
646/// The end of a directed string is indicated by SDS with a parameter value of 0. Every end of such a string
647/// re-establishes the next higher level of nesting (the one in effect prior to the string just ended). The
648/// direction is re-established to that in effect prior to the string just ended. The active data position is
649/// moved to the character position following the characters of the string just ended.
650///
651/// ### Note 1
652/// The effect of receiving a CVT, HT, SCP, SPD or VT control function within an SDS string is not defined
653/// by this Standard.
654///
655/// ### Note 2
656/// The control functions for area definition (DAQ, EPA, ESA, SPA, SSA) should not be used within an SDS
657/// string.
658pub fn directed(string_direction: StringDirection) -> ControlSequence {
659    ControlSequence::new(&[&string_direction.to_string()], "]")
660}
661
662#[derive(Copy, Clone, Debug)]
663pub enum StringDirection {
664    End,
665    StartLeftToRight,
666    StartRightToLeft,
667}
668
669impl Display for StringDirection {
670    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
671        write!(f, "{}", match self {
672            StringDirection::End => "0",
673            StringDirection::StartLeftToRight => "1",
674            StringDirection::StartRightToLeft => "2",
675        })
676    }
677}
678
679/// # SIMD - Select implicit movement direction
680///
681/// SIMD is used to select the direction of implicit movement of the data position relative to the character
682/// progression. The direction selected remains in effect until the next occurrence of SIMD.
683pub fn select_implicit(movement_direction: MovementDirection) -> ControlSequence {
684    ControlSequence::new(&[&movement_direction.to_string()], "^")
685}
686
687#[derive(Copy, Clone, Debug)]
688pub enum MovementDirection {
689    Same,
690    Opposite,
691}
692
693impl Display for MovementDirection {
694    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
695        write!(f, "{}", match self {
696            MovementDirection::Same => "0",
697            MovementDirection::Opposite => "1",
698        })
699    }
700}
701
702/// # SGR - Select graphic rendition
703///
704/// SGR is used to establish one or more graphic rendition aspects for subsequent text. The established
705/// aspects remain in effect until the next occurrence of SGR in the data stream, depending on the setting of
706/// the GRAPHIC RENDITION COMBINATION MODE (GRCM).
707///
708/// ### Example
709/// ```
710///
711/// // Direct format
712/// use coded_chars::presentation::select_graphic;
713/// println!("Hello {}{}{} !", select_graphic().fg_red().bold().underline(), "World", select_graphic().default());
714/// ```
715pub fn select_graphic() -> GraphicSelection {
716    GraphicSelection::new()
717}
718
719#[derive(Clone)]
720pub struct GraphicSelection {
721    modes: Vec<String>,
722}
723impl GraphicSelection {
724    pub fn new() -> Self { Self { modes: vec![] } }
725
726    /// Default rendition (implementation-defined), cancels the effect of any preceding occurrence of SGR in
727    /// the data stream regardless of the setting of the GRAPHIC RENDITION COMBINATION MODE (GRCM).
728    pub fn default(&mut self) -> &mut Self { self.add("0") }
729
730    /// Bold or increased intensity
731    pub fn bold(&mut self) -> &mut Self { self.add("1") }
732
733    /// Faint, decreased intensity or second color
734    pub fn faint(&mut self) -> &mut Self { self.add("2") }
735    pub fn italic(&mut self) -> &mut Self { self.add("3") }
736    pub fn underline(&mut self) -> &mut Self { self.add("4") }
737
738    /// Slowly blinking (less than 150/minute)
739    pub fn slow_blink(&mut self) -> &mut Self { self.add("5") }
740
741    /// Rapidly blinking (150/minute or more)
742    pub fn fast_blink(&mut self) -> &mut Self { self.add("6") }
743    pub fn negative(&mut self) -> &mut Self { self.add("7") }
744    pub fn conceal(&mut self) -> &mut Self { self.add("8") }
745
746    /// Crossed-out (characters still legible but marked as to be deleted)
747    pub fn cross(&mut self) -> &mut Self { self.add("9") }
748    pub fn primary_font(&mut self) -> &mut Self { self.add("10") }
749    pub fn alter1_font(&mut self) -> &mut Self { self.add("11") }
750    pub fn alter2_font(&mut self) -> &mut Self { self.add("12") }
751    pub fn alter3_font(&mut self) -> &mut Self { self.add("13") }
752    pub fn alter4_font(&mut self) -> &mut Self { self.add("14") }
753    pub fn alter5_font(&mut self) -> &mut Self { self.add("15") }
754    pub fn alter6_font(&mut self) -> &mut Self { self.add("16") }
755    pub fn alter7_font(&mut self) -> &mut Self { self.add("17") }
756    pub fn alter8_font(&mut self) -> &mut Self { self.add("18") }
757    pub fn alter9_font(&mut self) -> &mut Self { self.add("19") }
758    pub fn gothic_font(&mut self) -> &mut Self { self.add("20") }
759    pub fn double_underline(&mut self) -> &mut Self { self.add("21") }
760
761    /// Normal color or normal intensity
762    pub fn not_bold_or_faint(&mut self) -> &mut Self { self.add("22") }
763
764    /// Not italicized, not gothic font
765    pub fn not_italic(&mut self) -> &mut Self { self.add("23") }
766
767    /// Not underline (neither singly or doubly)
768    pub fn not_underline(&mut self) -> &mut Self { self.add("24") }
769
770    /// Steady (not blinking)
771    pub fn not_blink(&mut self) -> &mut Self { self.add("25") }
772
773    /// Positive image
774    pub fn not_negative(&mut self) -> &mut Self { self.add("27") }
775
776    /// Revealed characters
777    pub fn not_conceal(&mut self) -> &mut Self { self.add("28") }
778    pub fn not_cross(&mut self) -> &mut Self { self.add("29") }
779    pub fn fg_black(&mut self) -> &mut Self { self.add("30") }
780    pub fn fg_red(&mut self) -> &mut Self { self.add("31") }
781    pub fn fg_green(&mut self) -> &mut Self { self.add("32") }
782    pub fn fg_yellow(&mut self) -> &mut Self { self.add("33") }
783    pub fn fg_blue(&mut self) -> &mut Self { self.add("34") }
784    pub fn fg_magenta(&mut self) -> &mut Self { self.add("35") }
785    pub fn fg_cyan(&mut self) -> &mut Self { self.add("36") }
786    pub fn fg_gray(&mut self) -> &mut Self { self.add("37") }
787    pub fn fg_color(&mut self) -> &mut Self { self.add("38") }
788    pub fn fg_default(&mut self) -> &mut Self { self.add("39") }
789    pub fn bg_black(&mut self) -> &mut Self { self.add("40") }
790    pub fn bg_red(&mut self) -> &mut Self { self.add("41") }
791    pub fn bg_green(&mut self) -> &mut Self { self.add("42") }
792    pub fn bg_yellow(&mut self) -> &mut Self { self.add("43") }
793    pub fn bg_blue(&mut self) -> &mut Self { self.add("44") }
794    pub fn bg_magenta(&mut self) -> &mut Self { self.add("45") }
795    pub fn bg_cyan(&mut self) -> &mut Self { self.add("46") }
796    pub fn bg_gray(&mut self) -> &mut Self { self.add("47") }
797    pub fn bg_color(&mut self) -> &mut Self { self.add("48") }
798    pub fn bg_default(&mut self) -> &mut Self { self.add("49") }
799    pub fn frame(&mut self) -> &mut Self { self.add("51") }
800    pub fn encircle(&mut self) -> &mut Self { self.add("52") }
801    pub fn overline(&mut self) -> &mut Self { self.add("53") }
802    pub fn not_frame_not_encircle(&mut self) -> &mut Self { self.add("54") }
803    pub fn not_overline(&mut self) -> &mut Self { self.add("55") }
804    pub fn ideogram_underline(&mut self) -> &mut Self { self.add("60") }
805    pub fn ideogram_double_underline(&mut self) -> &mut Self { self.add("61") }
806    pub fn ideogram_overline(&mut self) -> &mut Self { self.add("62") }
807    pub fn ideogram_double_overline(&mut self) -> &mut Self { self.add("63") }
808    pub fn ideogram_stress_marking(&mut self) -> &mut Self { self.add("64") }
809    pub fn ideogram_cancel(&mut self) -> &mut Self { self.add("65") }
810    pub fn get(&self) -> ControlSequence {
811        ControlSequence::new(&self.modes.iter().map(|s| s.as_str()).collect::<Vec<_>>(), "m")
812    }
813    fn add(&mut self, s: &str) -> &mut Self {
814        self.modes.push(s.to_string());
815        self
816    }
817}
818
819impl Display for GraphicSelection {
820    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
821        write!(f, "{}", self.get())
822    }
823}
824
825/// Format a string with the specified `SGR` sequence.
826///
827/// The string is terminated with the sequence `\x1b[0m` to reset the style.
828///
829/// ### Example
830/// ```
831/// use coded_chars::presentation::{format_str, select_graphic};
832/// let formatted = format_str(
833///     "World",
834///     select_graphic().fg_red().bold().underline()
835///  );
836/// println!("Hello {} !", formatted);
837/// ```
838pub fn format_str(str: &str, format: &GraphicSelection) -> String {
839    format!("{}{}{}", format, str, select_graphic().default())
840}
841
842/// # SHS - Select character spacing
843///
844/// SHS is used to establish the character spacing for subsequent text. The established spacing remains in
845/// effect until the next occurrence of SHS or of SET CHARACTER SPACING (SCS) or of SPACING
846/// INCREMENT (SPI) in the data stream.
847pub fn select_spacing(character_spacing: CharacterSpacing) -> ControlSequence {
848    ControlSequence::new(&[&character_spacing.to_string()], " K")
849}
850
851#[derive(Copy, Clone, Debug)]
852pub enum CharacterSpacing {
853    Per25mm10Chars,
854    Per25mm12Chars,
855    Per25mm15Chars,
856    Per25mm16Chars,
857    Per25mm3Chars,
858    Per50mm9Chars,
859    Per25mm4Chars,
860}
861
862impl Display for CharacterSpacing {
863    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
864        write!(f, "{}", match self {
865            CharacterSpacing::Per25mm10Chars => "0",
866            CharacterSpacing::Per25mm12Chars => "1",
867            CharacterSpacing::Per25mm15Chars => "2",
868            CharacterSpacing::Per25mm16Chars => "3",
869            CharacterSpacing::Per25mm3Chars => "4",
870            CharacterSpacing::Per50mm9Chars => "5",
871            CharacterSpacing::Per25mm4Chars => "6",
872        })
873    }
874}
875
876/// # SLH - Set line home
877///
878/// If the DEVICE COMPONENT SELECT MODE is set to PRESENTATION, SLH is used to establish at
879/// character position n in the active line (the line that contains the active presentation position) and lines of
880/// subsequent text in the presentation component the position to which the active presentation position will
881/// be moved by subsequent occurrences of CARRIAGE RETURN (CR), DELETE LINE (DL), INSERT
882/// LINE (IL) or NEXT LINE (NEL) in the data stream. In the case of a
883/// device without data component, it is also the position ahead of which no implicit movement of the active
884/// presentation position shall occur.
885///
886/// If the DEVICE COMPONENT SELECT MODE is set to DATA, SLH is used to establish at character
887/// position n in the active line (the line that contains the active data position) and lines of subsequent text
888/// in the data component the position to which the active data position will be moved by subsequent
889/// occurrences of CARRIAGE RETURN (CR), DELETE LINE (DL), INSERT LINE (IL) or NEXT LINE
890/// (NEL) in the data stream. It is also the position ahead of which no
891/// implicit movement of the active data position shall occur.
892///
893/// The established position is called the line home position and remains in effect until the next occurrence
894/// of SLH in the data stream.
895pub fn line_home(c: usize) -> ControlSequence {
896    ControlSequence::new(&[&c.to_string()], " U")
897}
898
899/// # SLL - Set line limit
900///
901/// If the DEVICE COMPONENT SELECT MODE is set to PRESENTATION, SLL is used to establish at
902/// character position n in the active line (the line that contains the active presentation position) and lines of
903/// subsequent text in the presentation component the position to which the active presentation position will
904/// be moved by subsequent occurrences of CARRIAGE RETURN (CR), or NEXT LINE (NEL) in the data
905/// stream if the parameter value of SELECT IMPLICIT MOVEMENT DIRECTION (SIMD) is equal to 1;
906/// where n equals the value of Pn. In the case of a device without data component, it is also the position
907/// beyond which no implicit movement of the active presentation position shall occur.
908///
909/// If the DEVICE COMPONENT SELECT MODE is set to DATA, SLL is used to establish at character
910/// position n in the active line (the line that contains the active data position) and lines of subsequent text
911/// in the data component the position beyond which no implicit movement of the active data position shall
912/// occur. It is also the position in the data component to which the active data position will be moved by
913/// subsequent occurrences of CR or NEL in the data stream, if the parameter value of SELECT IMPLICIT
914/// MOVEMENT DIRECTION (SIMD) is equal to 1.
915///
916/// The established position is called the line limit position and remains in effect until the next occurrence
917/// of SLL in the data stream.
918pub fn line_limit(n: usize) -> ControlSequence {
919    ControlSequence::new(&[&n.to_string()], " V")
920}
921
922/// # SLS - Set line spacing
923///
924/// SLS is used to establish the line spacing for subsequent text. The established spacing remains in effect
925/// until the next occurrence of SLS or of SELECT LINE SPACING (SVS) or of SPACING INCREMENT
926/// (SPI) in the data stream.
927///
928/// The unit in which the parameter value is expressed is that established by the parameter value of SELECT
929/// SIZE UNIT (SSU).
930pub fn line_spacing(n: usize) -> ControlSequence {
931    ControlSequence::new(&[&n.to_string()], " h")
932}
933
934/// # SPD - Select presentation directions
935///
936/// SPD is used to select the line orientation, the line progression, and the character path in the presentation
937/// component. It is also used to update the content of the presentation component and the content of the
938/// data component. This takes effect immediately.
939pub fn select_directions(
940    line_orientation: LineOrientation,
941    line_progression: CharacterPath,
942    character_path: CharacterPath,
943    path_effect: PathEffect,
944) -> ControlSequence {
945    ControlSequence::new(&[&spd_ps1(line_orientation, line_progression, character_path).to_string(), &path_effect.to_string()], " S")
946}
947
948#[derive(Copy, Clone, Debug)]
949pub enum LineOrientation {
950    Horizontal,
951    Vertical,
952}
953
954fn spd_ps1(line_orientation: LineOrientation, line_progression: CharacterPath, character_path: CharacterPath) -> usize {
955    match line_orientation {
956        LineOrientation::Horizontal => {
957            match line_progression {
958                CharacterPath::LeftToRight => {
959                    match character_path {
960                        CharacterPath::LeftToRight => 0,
961                        CharacterPath::RightToLeft => 3,
962                    }
963                }
964                CharacterPath::RightToLeft => {
965                    match character_path {
966                        CharacterPath::LeftToRight => 6,
967                        CharacterPath::RightToLeft => 5,
968                    }
969                }
970            }
971        }
972        LineOrientation::Vertical => {
973            match line_progression {
974                CharacterPath::LeftToRight => {
975                    match character_path {
976                        CharacterPath::LeftToRight => 2,
977                        CharacterPath::RightToLeft => 4,
978                    }
979                }
980                CharacterPath::RightToLeft => {
981                    match character_path {
982                        CharacterPath::LeftToRight => 1,
983                        CharacterPath::RightToLeft => 7,
984                    }
985                }
986            }
987        }
988    }
989}
990
991/// # SPH - Set page home
992///
993/// If the DEVICE COMPONENT SELECT MODE is set to PRESENTATION, SPH is used to establish at
994/// line position n in the active page (the page that contains the active presentation position) and subsequent
995/// pages in the presentation component the position to which the active presentation position will be moved
996/// by subsequent occurrences of FORM FEED (FF) in the data stream In
997/// the case of a device without data component, it is also the position ahead of which no implicit movement
998/// of the active presentation position shall occur.
999///
1000/// If the DEVICE COMPONENT SELECT MODE is set to DATA, SPH is used to establish at line position
1001/// `n` in the active page (the page that contains the active data position) and subsequent pages in the data
1002/// component the position to which the active data position will be moved by subsequent occurrences of
1003/// FORM FEED (FF) in the data stream. It is also the position ahead of
1004/// which no implicit movement of the active presentation position shall occur.
1005///
1006/// The established position is called the page home position and remains in effect until the next occurrence
1007/// of SPH in the data stream.
1008pub fn page_home(n: usize) -> ControlSequence {
1009    ControlSequence::new(&[&n.to_string()], " i")
1010}
1011
1012/// # SPI - Spacing increment
1013///
1014/// SPI is used to establish the line spacing and the character spacing for subsequent text. The established
1015/// line spacing remains in effect until the next occurrence of SPI or of SET LINE SPACING (SLS) or of
1016/// SELECT LINE SPACING (SVS) in the data stream. The established character spacing remains in effect
1017/// until the next occurrence of SET CHARACTER SPACING (SCS) or of SELECT CHARACTER
1018/// SPACING (SHS) in the data stream.
1019///
1020/// The unit in which the parameter values are expressed is that established by the parameter value of
1021/// SELECT SIZE UNIT (SSU).
1022pub fn spacing_increment(line_spacing: usize, character_spacing: usize) -> ControlSequence {
1023    ControlSequence::new(&[&line_spacing.to_string(), &character_spacing.to_string()], " G")
1024}
1025
1026/// # SPL - Set page limit
1027///
1028/// If the DEVICE COMPONENT SELECT MODE is set to PRESENTATION, SPL is used to establish at
1029/// line position n in the active page (the page that contains the active presentation position) and pages of
1030/// subsequent text in the presentation component the position beyond which the active presentation position
1031/// can normally not be moved In the case of a device without data
1032/// component, it is also the position beyond which no implicit movement of the active presentation position
1033/// shall occur.
1034///
1035/// If the DEVICE COMPONENT SELECT MODE is set to DATA, SPL is used to establish at line position
1036/// n in the active page (the page that contains the active data position) and pages of subsequent text in the
1037/// data component the position beyond which no implicit movement of the active data position shall occur.
1038///
1039/// The established position is called the page limit position and remains in effect until the next occurrence
1040/// of SPL in the data stream.
1041pub fn page_limit(n: usize) -> ControlSequence {
1042    ControlSequence::new(&[&n.to_string()], " j")
1043}
1044
1045
1046/// # SPQR - Select print quality and rapidity
1047///
1048/// SPQR is used to select the relative print quality and the print speed for devices the output quality and
1049/// speed of which are inversely related. The selected values remain in effect until the next occurrence of
1050/// SPQR in the data stream.
1051pub fn print_quality(print_quality: PrintQuality) -> ControlSequence {
1052    ControlSequence::new(&[&print_quality.to_string()], " X")
1053}
1054
1055#[derive(Copy, Clone, Debug)]
1056pub enum PrintQuality {
1057    /// Highest available print quality, low print speed.
1058    Highest,
1059    /// Medium print quality, medium print speed.
1060    Medium,
1061    /// Draft print quality, highest available print speed.
1062    Draft,
1063}
1064
1065impl Display for PrintQuality {
1066    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1067        write!(f, "{}", match self {
1068            PrintQuality::Highest => "0",
1069            PrintQuality::Medium => "1",
1070            PrintQuality::Draft => "2",
1071        })
1072    }
1073}
1074
1075/// # SRCS - Set reduced character separation
1076///
1077/// SRCS is used to establish reduced inter-character escapement for subsequent text. The established
1078/// reduced escapement remains in effect until the next occurrence of SRCS or of SET ADDITIONAL
1079/// CHARACTER SEPARATION (SACS) in the data stream or until it is reset to the default value by a
1080/// subsequent occurrence of CARRIAGE RETURN/LINE FEED (CR/LF) or of NEXT LINE (NEL) in the
1081/// data stream.
1082///
1083/// `n` specifies the number of units by which the inter-character escapement is reduced.
1084///
1085/// The unit in which the parameter value is expressed is that established by the parameter value of SELECT
1086/// SIZE UNIT (SSU).
1087pub fn reduce_separation(n: usize) -> ControlSequence {
1088    ControlSequence::new(&[&n.to_string()], " f")
1089}
1090
1091/// # SRS - Start reversed string
1092///
1093/// SRS is used to establish in the data component the beginning and the end of a string of characters as well
1094/// as the direction of the string. This direction is opposite to that currently established. The indicated string
1095/// follows the preceding text. The established character progression is not affected.
1096///
1097/// The beginning of a reversed string is indicated by SRS with a parameter value of 1. A reversed string
1098/// may contain one or more nested strings. These nested strings may be reversed strings the beginnings of
1099/// which are indicated by SRS with a parameter value of 1, or directed strings the beginnings of which are
1100/// indicated by START DIRECTED STRING (SDS) with a parameter value not equal to 0. Every
1101/// beginning of such a string invokes the next deeper level of nesting.
1102///
1103/// This Standard does not define the location of the active data position within any such nested string.
1104///
1105/// The end of a reversed string is indicated by SRS with a parameter value of 0. Every end of such a string
1106/// re-establishes the next higher level of nesting (the one in effect prior to the string just ended). The
1107/// direction is re-established to that in effect prior to the string just ended. The active data position is
1108/// moved to the character position following the characters of the string just ended.
1109///
1110/// ### Note 1
1111/// The effect of receiving a CVT, HT, SCP, SPD or VT control function within an SRS string is not defined
1112/// by this Standard.
1113///
1114/// ### Note 2
1115/// The control functions for area definition (DAQ, EPA, ESA, SPA, SSA) should not be used within an SRS
1116/// string.
1117pub fn reversed(string_reversion: StringReversion) -> ControlSequence {
1118    ControlSequence::new(&[&string_reversion.to_string()], "[")
1119}
1120
1121#[derive(Copy, Clone, Debug)]
1122pub enum StringReversion {
1123    End,
1124    BeginReverse,
1125}
1126
1127impl Display for StringReversion {
1128    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1129        write!(f, "{}", match self {
1130            Self::End => "0",
1131            Self::BeginReverse => "1",
1132        })
1133    }
1134}
1135
1136/// # SSU - Select size unit
1137///
1138/// SSU is used to establish the unit in which the numeric parameters of certain control functions are
1139/// expressed. The established unit remains in effect until the next occurrence of SSU in the data stream.
1140pub fn select_size_unit(size_unit: SizeUnit) -> ControlSequence {
1141    ControlSequence::new(&[&size_unit.to_string()], " I")
1142}
1143
1144#[derive(Copy, Clone, Debug)]
1145pub enum SizeUnit {
1146    Character,
1147    Millimeter,
1148    ComputerDeciPoint,
1149    DeciDidot,
1150    Mil,
1151    BasicMeasuringUnit,
1152    Micrometer,
1153    Pixel,
1154    DeciPoint,
1155}
1156
1157impl Display for SizeUnit {
1158    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1159        write!(f, "{}", match self {
1160            SizeUnit::Character => "0",
1161            SizeUnit::Millimeter => "1",
1162            SizeUnit::ComputerDeciPoint => "2",
1163            SizeUnit::DeciDidot => "3",
1164            SizeUnit::Mil => "4",
1165            SizeUnit::BasicMeasuringUnit => "5",
1166            SizeUnit::Micrometer => "6",
1167            SizeUnit::Pixel => "7",
1168            SizeUnit::DeciPoint => "8",
1169        })
1170    }
1171}
1172
1173/// # SSW - Set space width
1174///
1175/// SSW is used to establish for subsequent text the character escapement associated with the character
1176/// SPACE. The established escapement remains in effect until the next occurrence of SSW in the data
1177/// stream or until it is reset to the default value by a subsequent occurrence of CARRIAGE RETURN/LINE
1178/// FEED (CR/LF), CARRIAGE RETURN/FORM FEED (CR/FF), or of NEXT LINE (NEL) in the data stream.
1179///
1180/// `n` specifies the escapement.
1181///
1182/// The unit in which the parameter value is expressed is that established by the parameter value of SELECT
1183/// SIZE UNIT (SSU).
1184///
1185/// The default character escapement of SPACE is specified by the most recent occurrence of SET
1186/// CHARACTER SPACING (SCS) or of SELECT CHARACTER SPACING (SHS) or of SELECT
1187/// SPACING INCREMENT (SPI) in the data stream if the current font has constant spacing, or is specified
1188/// by the nominal width of the character SPACE in the current font if that font has proportional spacing.
1189pub fn space_width(n: usize) -> ControlSequence {
1190    ControlSequence::new(&[&n.to_string()], " [")
1191}
1192
1193/// # STAB - Selective tabulation
1194///
1195/// STAB causes subsequent text in the presentation component to be aligned according to the position and
1196/// the properties of a tabulation stop which is selected from a list according to the value of the parameter.
1197///
1198/// The use of this control function and means of specifying a list of tabulation stops to be referenced by the
1199/// control function are specified in other standards, for example ISO 8613-6.
1200pub fn select_tabulation(n: usize) -> ControlSequence {
1201    ControlSequence::new(&[&n.to_string()], " ^")
1202}
1203
1204/// # SVS - Select line spacing
1205///
1206/// SVS is used to establish the line spacing for subsequent text. The established spacing remains in effect
1207/// until the next occurrence of SVS or of SET LINE SPACING (SLS) or of SPACING INCREMENT (SPI)
1208/// in the data stream.
1209pub fn select_line_spacing(line_spacing: LineSpacing) -> ControlSequence {
1210    ControlSequence::new(&[&line_spacing.to_string()], " L")
1211}
1212
1213#[derive(Copy, Clone, Debug)]
1214pub enum LineSpacing {
1215    Per25mm6Lines,
1216    Per25mm4Lines,
1217    Per25mm3Lines,
1218    Per25mm12Lines,
1219    Per25mm8Lines,
1220    Per30mm6Lines,
1221    Per30mm4Lines,
1222    Per30mm3Lines,
1223    Per30mm12Lines,
1224    Per25mm2Lines,
1225}
1226
1227impl Display for LineSpacing {
1228    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1229        write!(f, "{}", match self {
1230            LineSpacing::Per25mm6Lines => "0",
1231            LineSpacing::Per25mm4Lines => "1",
1232            LineSpacing::Per25mm3Lines => "2",
1233            LineSpacing::Per25mm12Lines => "3",
1234            LineSpacing::Per25mm8Lines => "4",
1235            LineSpacing::Per30mm6Lines => "5",
1236            LineSpacing::Per30mm4Lines => "6",
1237            LineSpacing::Per30mm3Lines => "7",
1238            LineSpacing::Per30mm12Lines => "8",
1239            LineSpacing::Per25mm2Lines => "9",
1240        })
1241    }
1242}
1243
1244
1245/// # TAC - Tabulation aligned centred
1246///
1247/// TAC causes a character tabulation stop calling for centring to be set at character position n in the active
1248/// line (the line that contains the active presentation position) and lines of subsequent text in the
1249/// presentation component, where n equals the value of Pn. TAC causes the replacement of any tabulation
1250/// stop previously set at that character position, but does not affect other tabulation stops.
1251///
1252/// A text string centred upon a tabulation stop set by TAC will be positioned so that the (trailing edge of
1253/// the) first graphic character and the (leading edge of the) last graphic character are at approximately equal
1254/// distances from the tabulation stop.
1255pub fn align_center(n: usize) -> ControlSequence {
1256    ControlSequence::new(&[&n.to_string()], " b")
1257}
1258
1259/// # TALE - Tabulation aligned leading edge
1260///
1261/// TALE causes a character tabulation stop calling for leading edge alignment to be set at character
1262/// position n in the active line (the line that contains the active presentation position) and lines of
1263/// subsequent text in the presentation component, where n equals the value of Pn. TALE causes the
1264/// replacement of any tabulation stop previously set at that character position, but does not affect other
1265/// tabulation stops.
1266///
1267/// A text string aligned with a tabulation stop set by TALE will be positioned so that the (leading edge of
1268/// the) last graphic character of the string is placed at the tabulation stop.
1269pub fn align_leading(n: usize) -> ControlSequence {
1270    ControlSequence::new(&[&n.to_string()], " a")
1271}
1272
1273/// # TATE - Tabulation aligned trailing edge
1274///
1275/// TATE causes a character tabulation stop calling for trailing edge alignment to be set at character
1276/// position n in the active line (the line that contains the active presentation position) and lines of
1277/// subsequent text in the presentation component, where n equals the value of Pn. TATE causes the
1278/// replacement of any tabulation stop previously set at that character position, but does not affect other
1279/// tabulation stops.
1280///
1281/// A text string aligned with a tabulation stop set by TATE will be positioned so that the (trailing edge of
1282/// the) first graphic character of the string is placed at the tabulation stop.
1283pub fn align_trailing(n: usize) -> ControlSequence {
1284    ControlSequence::new(&[&n.to_string()], " `")
1285}
1286
1287/// # TCC - Tabulation centred on character
1288///
1289/// TCC causes a character tabulation stop calling for alignment of a target graphic character to be set at
1290/// character position `l` in the active line (the line that contains the active presentation position) and lines of
1291/// subsequent text in the presentation component, and the target character
1292/// about which centring is to be performed is specified by `ascii`. TCC causes the replacement of any
1293/// tabulation stop previously set at that character position, but does not affect other tabulation stops.
1294///
1295/// The positioning of a text string aligned with a tabulation stop set by TCC will be determined by the first
1296/// occurrence in the string of the target graphic character; that character will be centred upon the tabulation
1297/// stop. If the target character does not occur within the string, then the trailing edge of the first character
1298/// of the string will be positioned at the tabulation stop.
1299///
1300/// The value of `ascii` indicates the code table position (binary value) of the target character in the currently
1301/// invoked code. For a 7-bit code, the permissible range of values is 32 to 127; for an 8-bit code, the
1302/// permissible range of values is 32 to 127 and 160 to 255.
1303pub fn tabulation_center_on_char(l: usize, ascii: usize) -> ControlSequence {
1304    ControlSequence::new(&[&l.to_string(), &ascii.to_string()], " c")
1305}
1306
1307/// # TSS - Thin space specification
1308///
1309/// TSS is used to establish the width of a thin space for subsequent text. The established width remains in
1310/// effect until the next occurrence of TSS in the data stream, see annex C.
1311///
1312/// `width` specifies the width of the thin space.
1313///
1314/// The unit in which the parameter value is expressed is that established by the parameter value of SELECT
1315/// SIZE UNIT (SSU).
1316pub fn specify_thin_space(width: usize) -> ControlSequence {
1317    ControlSequence::new(&[&width.to_string()], " E")
1318}
1319
1320
1321#[cfg(test)]
1322mod tests {
1323    use super::*;
1324
1325    #[test]
1326    fn test_space_width() {
1327        let cs = space_width(5);
1328        assert_eq!(cs.to_string(), "\x1b[5 [");
1329    }
1330
1331    #[test]
1332    fn test_select_tabulation() {
1333        let cs = select_tabulation(3);
1334        assert_eq!(cs.to_string(), "\x1b[3 ^");
1335    }
1336
1337    #[test]
1338    fn test_select_line_spacing() {
1339        let cs = select_line_spacing(LineSpacing::Per25mm6Lines);
1340        assert_eq!(cs.to_string(), "\x1b[0 L");
1341        let cs = select_line_spacing(LineSpacing::Per30mm12Lines);
1342        assert_eq!(cs.to_string(), "\x1b[8 L");
1343    }
1344
1345    #[test]
1346    fn test_line_spacing_display() {
1347        let ls = LineSpacing::Per25mm4Lines;
1348        assert_eq!(ls.to_string(), "1");
1349        let ls = LineSpacing::Per30mm3Lines;
1350        assert_eq!(ls.to_string(), "7");
1351    }
1352
1353    #[test]
1354    fn test_align_center() {
1355        let cs = align_center(10);
1356        assert_eq!(cs.to_string(), "\x1b[10 b");
1357    }
1358
1359    #[test]
1360    fn test_align_leading() {
1361        let cs = align_leading(15);
1362        assert_eq!(cs.to_string(), "\x1b[15 a");
1363    }
1364
1365    #[test]
1366    fn test_align_trailing() {
1367        let cs = align_trailing(20);
1368        assert_eq!(cs.to_string(), "\x1b[20 `");
1369    }
1370
1371    #[test]
1372    fn test_tabulation_center_on_char() {
1373        let cs = tabulation_center_on_char(25, 65);
1374        assert_eq!(cs.to_string(), "\x1b[25;65 c");
1375    }
1376
1377    #[test]
1378    fn test_specify_thin_space() {
1379        let cs = specify_thin_space(2);
1380        assert_eq!(cs.to_string(), "\x1b[2 E");
1381    }
1382}