swf_emitter/
text.rs

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// TODO: Remove unused variants (`dead_code` should not be allowed)
12#[allow(dead_code)]
13#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
14pub(crate) enum DefineFontVersion {
15  // `Font1` corresponds to `DefineGlyphFont` and is handled separately.
16  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    // Skip bits [4, 6]
100    | (1 << 7); // Bit 7 must be set (TextRecordType)
101  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  // Skip bits [2, 7]
155  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}