Skip to content

Commit

Permalink
fix: handling symbol fonts (#54)
Browse files Browse the repository at this point in the history
  • Loading branch information
francisdb authored Apr 2, 2024
1 parent 2d04b7e commit d46d60f
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 6 deletions.
95 changes: 90 additions & 5 deletions src/vpx/gameitem/font.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,29 @@ use fake::Dummy;
use serde::{Deserialize, Serialize};
use std::collections::HashSet;

// from https://github.com/wine-mirror/wine/blob/f38a32e64c00600a5252fe0b9ca1ca42208bd6fe/dlls/oleaut32/olefont.c#L1546-L1566
/************************************************************************
* OLEFontImpl_Load (IPersistStream)
*
* See Windows documentation for more details on IPersistStream methods.
*
* This is the format of the standard font serialization as far as I
* know
*
* Offset Type Value Comment
* 0x0000 Byte Unknown Probably a version number, contains 0x01
* 0x0001 Short Charset Charset value from the FONTDESC structure
* 0x0003 Byte Attributes Flags defined as follows:
* 00000010 - Italic
* 00000100 - Underline
* 00001000 - Strikethrough
* 0x0004 Short Weight Weight value from FONTDESC structure
* 0x0006 DWORD size "Low" portion of the cySize member of the FONTDESC
* structure/
* 0x000A Byte name length Length of the font name string (no null character)
* 0x000B String name Name of the font (ASCII, no nul character)
*/

/**
* The style of the font.
* This is serialized as a bitfield, so multiple styles can be combined.
Expand Down Expand Up @@ -60,8 +83,49 @@ impl FontStyle {
}
}

const EXPECTED_FONTDESC_VERSION: u8 = 0x01;

/// Standard Windows characters (ANSI).
pub const CHARSET_ANSI: u16 = 0;

/// Default character set.
pub const CHARSET_DEFAULT: u16 = 1;

/// The symbol character set.
pub const CHARSET_SYMBOL: u16 = 2;

/// Double-byte character set (DBCS) unique to the Japanese version of Windows.
pub const CHARSET_JAPANESE: u16 = 128;

/// Double-byte character set (DBCS) unique to the Korean version of Windows.
pub const CHARSET_KOREAN: u16 = 129;

/// Double-byte character set (DBCS) unique to the Simplified Chinese version of Windows.
pub const CHARSET_SIMPLIFIED_CHINESE: u16 = 134;

/// Double-byte character set (DBCS) unique to the Traditional Chinese version of Windows.
pub const CHARSET_TRADITIONAL_CHINESE: u16 = 136;

/// Extended characters normally displayed by Microsoft MS-DOS applications.
pub const CHARSET_EXTENDED: u16 = 255;

/// This is a font reference some primitives use.
/// In vpinball represented as serialized win32 FONTDESC struct
#[derive(PartialEq, Debug, Dummy)]
pub struct Font {
/// from https://learn.microsoft.com/en-us/windows/win32/lwef/fontcharset-property
/// An integer value that specifies the character set used by the font. The following are some
/// common settings for value:
/// 0 Standard Windows characters (ANSI).
/// 1 Default character set.
/// 2 The symbol character set.
/// 128 Double-byte character set (DBCS) unique to the Japanese version of Windows.
/// 129 Double-byte character set (DBCS) unique to the Korean version of Windows.
/// 134 Double-byte character set (DBCS) unique to the Simplified Chinese version of Windows.
/// 136 Double-byte character set (DBCS) unique to the Traditional Chinese version of Windows.
/// 255 Extended characters normally displayed by Microsoft MS-DOS applications.
/// For other character set values, consult the Platform SDK documentation.
charset: u16,
style: HashSet<FontStyle>,
weight: u16,
size: u32,
Expand All @@ -70,14 +134,21 @@ pub struct Font {

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub(crate) struct FontJson {
#[serde(skip_serializing_if = "Option::is_none")]
charset: Option<u16>,
style: HashSet<FontStyle>,
weight: u16,
size: u32,
name: String,
}
impl FontJson {
pub fn from_font(font: &Font) -> Self {
let charset = match font.charset {
CHARSET_ANSI => None,
_ => Some(font.charset),
};
Self {
charset,
style: font.style.clone(),
weight: font.weight,
size: font.size,
Expand All @@ -86,6 +157,7 @@ impl FontJson {
}
pub fn to_font(&self) -> Font {
Font {
charset: self.charset.unwrap_or(CHARSET_ANSI),
style: self.style.clone(),
weight: self.weight,
size: self.size,
Expand All @@ -95,8 +167,15 @@ impl FontJson {
}

impl Font {
pub fn new(style: HashSet<FontStyle>, weight: u16, size: u32, name: String) -> Self {
pub fn new(
charset: u16,
style: HashSet<FontStyle>,
weight: u16,
size: u32,
name: String,
) -> Self {
Self {
charset,
style,
weight,
size,
Expand All @@ -109,6 +188,7 @@ impl Default for Font {
fn default() -> Self {
// TODO get proper defaults
Self {
charset: CHARSET_ANSI,
style: HashSet::new(),
weight: 0,
size: 400,
Expand All @@ -119,14 +199,16 @@ impl Default for Font {

impl BiffRead for Font {
fn biff_read(reader: &mut BiffReader<'_>) -> Font {
let _header = reader.get_data(3); // always? 0x01, 0x0, 0x0

let version = reader.get_u8_no_remaining_update();
assert_eq!(version, EXPECTED_FONTDESC_VERSION, "Font version is not 1");
let charset = reader.get_u16_no_remaining_update();
let style = reader.get_u8_no_remaining_update();
let weight = reader.get_u16_no_remaining_update();
let size = reader.get_u32_no_remaining_update();
let name_len = reader.get_u8_no_remaining_update();
let name = reader.get_str_no_remaining_update(name_len as usize);
Font {
charset,
style: FontStyle::flags_to_styles(style),
weight,
size,
Expand All @@ -137,7 +219,9 @@ impl BiffRead for Font {

impl BiffWrite for Font {
fn biff_write(&self, writer: &mut BiffWriter) {
writer.write_data(&[0x01, 0x00, 0x00]);
// version?
writer.write_u8(EXPECTED_FONTDESC_VERSION);
writer.write_u16(self.charset);
writer.write_u8(FontStyle::styles_to_flags(&self.style));
writer.write_u16(self.weight);
writer.write_u32(self.size);
Expand All @@ -156,10 +240,11 @@ mod test {
#[test]
fn write_read_font() {
let font: Font = Font {
charset: CHARSET_SYMBOL,
style: HashSet::from([FontStyle::Bold, FontStyle::Italic, FontStyle::Underline]),
weight: 100,
size: 12,
name: "Arial Black".to_string(),
name: "Wingdings 3".to_string(),
};
let mut writer = BiffWriter::new();
Font::biff_write(&font, &mut writer);
Expand Down
3 changes: 2 additions & 1 deletion src/vpx/gameitem/textbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ mod tests {
use std::collections::HashSet;

use super::*;
use crate::vpx::gameitem::font::FontStyle;
use crate::vpx::gameitem::font::{FontStyle, CHARSET_ANSI};
use pretty_assertions::assert_eq;

#[test]
Expand All @@ -291,6 +291,7 @@ mod tests {
is_transparent: false,
is_dmd: Some(false),
font: Font::new(
CHARSET_ANSI,
HashSet::from([FontStyle::Bold, FontStyle::Underline]),
123,
456,
Expand Down
1 change: 1 addition & 0 deletions tests/vpx_read_extract_assemble_write_compare_all.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ mod test {
// * DieHard_272.vpx - primitive "BM_pAirDuctGate" has a NaN value for nx
// * Johnny Mnemonic (Williams 1995) VPW v1.0.2.vpx - animated frames that overlap with primitive names
// * Future Spa (Bally 1979) v4.3.vpx - NaN in table setup values
// * InvaderTable_2.260.vpx - Symbol fonts
let filtered: Vec<&PathBuf> = paths
.iter()
// .filter(|path| {
Expand Down

0 comments on commit d46d60f

Please sign in to comment.