1use std::convert::TryInto;
2use std::io;
3
4use swf_types as ast;
5
6use crate::basic_data_types::{emit_rect, emit_s_rgb8, emit_straight_s_rgba8};
7use crate::io_bits::{BitsWriter, WriteBits};
8use crate::primitives::{emit_le_f16, emit_le_i16, emit_le_u16, emit_le_u32, emit_u8};
9use crate::shape::emit_glyph;
10
11#[allow(dead_code)]
13#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
14pub(crate) enum DefineFontVersion {
15 Font2,
17 Font3,
18 Font4,
19}
20
21#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
22pub(crate) enum DefineFontInfoVersion {
23 FontInfo1,
24 FontInfo2,
25}
26
27#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
28pub(crate) enum DefineTextVersion {
29 Text1,
30 Text2,
31}
32
33pub(crate) fn csm_table_hint_to_code(value: ast::text::CsmTableHint) -> u8 {
34 match value {
35 ast::text::CsmTableHint::Thin => 0,
36 ast::text::CsmTableHint::Medium => 1,
37 ast::text::CsmTableHint::Thick => 2,
38 }
39}
40
41pub(crate) fn emit_language_code<W: io::Write>(writer: &mut W, value: ast::LanguageCode) -> io::Result<()> {
42 let code: u8 = match value {
43 ast::LanguageCode::Auto => 0,
44 ast::LanguageCode::Latin => 1,
45 ast::LanguageCode::Japanese => 2,
46 ast::LanguageCode::Korean => 3,
47 ast::LanguageCode::SimplifiedChinese => 4,
48 ast::LanguageCode::TraditionalChinese => 5,
49 };
50 emit_u8(writer, code)
51}
52
53pub(crate) fn grid_fitting_to_code(value: ast::text::GridFitting) -> u8 {
54 match value {
55 ast::text::GridFitting::None => 0,
56 ast::text::GridFitting::Pixel => 1,
57 ast::text::GridFitting::SubPixel => 2,
58 }
59}
60
61pub(crate) fn text_renderer_to_code(value: ast::text::TextRenderer) -> u8 {
62 match value {
63 ast::text::TextRenderer::Advanced => 1,
64 ast::text::TextRenderer::Normal => 0,
65 }
66}
67
68pub(crate) fn emit_text_record_string<W: io::Write>(
69 writer: &mut W,
70 value: &[ast::text::TextRecord],
71 index_bits: u32,
72 advance_bits: u32,
73 with_alpha: bool,
74) -> io::Result<()> {
75 for record in value {
76 emit_text_record(writer, record, index_bits, advance_bits, with_alpha)?;
77 }
78 emit_u8(writer, 0)
79}
80
81pub(crate) fn emit_text_record<W: io::Write>(
82 writer: &mut W,
83 value: &ast::text::TextRecord,
84 index_bits: u32,
85 advance_bits: u32,
86 with_alpha: bool,
87) -> io::Result<()> {
88 let has_offset_x = value.offset_x != 0;
89 let has_offset_y = value.offset_y != 0;
90 let has_color = value.color.is_some();
91 let has_font = value.font_id.is_some() && value.font_size.is_some();
92
93 #[allow(clippy::identity_op)]
94 let flags: u8 = 0
95 | (if has_offset_x { 1 << 0 } else { 0 })
96 | (if has_offset_y { 1 << 1 } else { 0 })
97 | (if has_color { 1 << 2 } else { 0 })
98 | (if has_font { 1 << 3 } else { 0 })
99 | (1 << 7); emit_u8(writer, flags)?;
102
103 if let Some(font_id) = value.font_id {
104 assert!(has_font);
105 emit_le_u16(writer, font_id)?;
106 }
107 if let Some(color) = value.color {
108 if with_alpha {
109 emit_straight_s_rgba8(writer, color)?;
110 } else {
111 assert!(color.a == u8::max_value());
112 emit_s_rgb8(
113 writer,
114 ast::SRgb8 {
115 r: color.r,
116 g: color.g,
117 b: color.b,
118 },
119 )?;
120 }
121 }
122 if has_offset_x {
123 emit_le_i16(writer, value.offset_x)?;
124 }
125 if has_offset_y {
126 emit_le_i16(writer, value.offset_y)?;
127 }
128 if let Some(font_size) = value.font_size {
129 assert!(has_font);
130 emit_le_u16(writer, font_size)?;
131 }
132 emit_u8(writer, value.entries.len().try_into().unwrap())?;
133 let mut bits_writer = BitsWriter::new(Vec::new());
134 for entry in &value.entries {
135 bits_writer.write_u32_bits(index_bits, entry.index.try_into().unwrap())?;
136 bits_writer.write_i32_bits(advance_bits, entry.advance)?;
137 }
138 writer.write_all(&bits_writer.into_inner()?)
139}
140
141pub(crate) fn emit_font_alignment_zone<W: io::Write>(
142 writer: &mut W,
143 value: &ast::text::FontAlignmentZone,
144) -> io::Result<()> {
145 assert!(value.data.len() < 256);
146
147 emit_u8(writer, value.data.len().try_into().unwrap())?;
148 for zone_data in &value.data {
149 emit_le_f16(writer, zone_data.origin)?;
150 emit_le_f16(writer, zone_data.size)?;
151 }
152 #[allow(clippy::identity_op)]
153 let flags: u8 = 0 | (if value.has_x { 1 << 0 } else { 0 }) | (if value.has_y { 1 << 1 } else { 0 });
154 emit_u8(writer, flags)
156}
157
158pub(crate) fn emit_offset_glyphs<W: io::Write>(writer: &mut W, value: &[ast::Glyph]) -> io::Result<bool> {
159 let mut end_offsets: Vec<usize> = Vec::with_capacity(value.len());
160 let mut glyph_writer: Vec<u8> = Vec::new();
161 for glyph in value {
162 emit_glyph(&mut glyph_writer, glyph)?;
163 end_offsets.push(glyph_writer.len());
164 }
165
166 let offset_table_len = end_offsets.len() + 1;
167 let short_offset_table_size = offset_table_len * std::mem::size_of::<u16>();
168 let max_offset_with_short_table = short_offset_table_size + glyph_writer.len();
169
170 let use_wide_offsets = max_offset_with_short_table > usize::from(u16::max_value());
171
172 if use_wide_offsets {
173 let wide_offset_table_size = offset_table_len * std::mem::size_of::<u32>();
174
175 emit_le_u32(writer, wide_offset_table_size.try_into().unwrap())?;
176 for end_offset in end_offsets {
177 emit_le_u32(writer, (wide_offset_table_size + end_offset).try_into().unwrap())?;
178 }
179 } else {
180 emit_le_u16(writer, short_offset_table_size.try_into().unwrap())?;
181 for end_offset in end_offsets {
182 emit_le_u16(writer, (short_offset_table_size + end_offset).try_into().unwrap())?;
183 }
184 }
185
186 writer.write_all(&glyph_writer)?;
187
188 Ok(use_wide_offsets)
189}
190
191pub(crate) fn emit_font_layout<W: io::Write>(writer: &mut W, value: &ast::text::FontLayout) -> io::Result<()> {
192 emit_le_u16(writer, value.ascent)?;
193 emit_le_u16(writer, value.descent)?;
194 emit_le_u16(writer, value.leading)?;
195 for advance in &value.advances {
196 emit_le_u16(writer, *advance)?;
197 }
198 for bound in &value.bounds {
199 emit_rect(writer, bound)?;
200 }
201 emit_le_u16(writer, value.kerning.len().try_into().unwrap())?;
202 for kerning_record in &value.kerning {
203 emit_kerning_record(writer, kerning_record)?;
204 }
205 Ok(())
206}
207
208pub(crate) fn emit_kerning_record<W: io::Write>(writer: &mut W, value: &ast::text::KerningRecord) -> io::Result<()> {
209 emit_le_u16(writer, value.left)?;
210 emit_le_u16(writer, value.right)?;
211 emit_le_i16(writer, value.adjustment)
212}
213
214pub(crate) fn emit_text_alignment<W: io::Write>(writer: &mut W, value: ast::text::TextAlignment) -> io::Result<()> {
215 let code: u8 = match value {
216 ast::text::TextAlignment::Center => 2,
217 ast::text::TextAlignment::Justify => 3,
218 ast::text::TextAlignment::Left => 0,
219 ast::text::TextAlignment::Right => 1,
220 };
221 emit_u8(writer, code)
222}