use hwpforge_foundation::{
Color, EmbossType, EmphasisType, EngraveType, FontIndex, HeadingType, HwpUnit, LineSpacingType,
OutlineType, ShadowType, StrikeoutShape, UnderlineType, VerticalPosition, WordBreakType,
};
use quick_xml::de::from_str;
use crate::error::{HwpxError, HwpxResult};
use crate::schema::header::{
HxBorderFill, HxCharPr, HxHead, HxNumbering, HxParaPr, HxStyle, HxTabPr,
};
use crate::style_store::{
parse_alignment, parse_hex_color, HwpxBorderFill, HwpxBorderLine, HwpxCharShape, HwpxFill,
HwpxFont, HwpxFontRef, HwpxParaShape, HwpxStyle, HwpxStyleStore,
};
use hwpforge_core::section::BeginNum;
#[derive(Debug)]
pub struct HeaderParseResult {
pub style_store: HwpxStyleStore,
pub begin_num: Option<BeginNum>,
}
pub fn parse_header(xml: &str) -> HwpxResult<HeaderParseResult> {
let head: HxHead = from_str(xml)
.map_err(|e| HwpxError::XmlParse { file: "header.xml".into(), detail: e.to_string() })?;
let begin_num = head.begin_num.map(|bn| BeginNum {
page: bn.page,
footnote: bn.footnote,
endnote: bn.endnote,
pic: bn.pic,
tbl: bn.tbl,
equation: bn.equation,
});
let mut store = HwpxStyleStore::new();
if let Some(ref_list) = &head.ref_list {
if let Some(fontfaces) = &ref_list.fontfaces {
for group in &fontfaces.groups {
for font in &group.fonts {
store.push_font(HwpxFont {
id: font.id,
face_name: font.face.clone(),
lang: group.lang.clone(),
});
}
}
}
if let Some(border_fills) = &ref_list.border_fills {
for bf in &border_fills.items {
store.push_border_fill(convert_border_fill(bf));
}
}
if let Some(char_props) = &ref_list.char_properties {
for cp in &char_props.items {
store.push_char_shape(convert_char_pr(cp));
}
}
if let Some(para_props) = &ref_list.para_properties {
for pp in ¶_props.items {
store.push_para_shape(convert_para_pr(pp));
}
}
if let Some(tab_props) = &ref_list.tab_properties {
for tp in &tab_props.items {
store.push_tab(convert_tab(tp));
}
}
if let Some(numberings) = &ref_list.numberings {
for ndef in &numberings.items {
store.push_numbering(convert_numbering(ndef));
}
}
if let Some(styles) = &ref_list.styles {
for style in &styles.items {
store.push_style(convert_style(style));
}
}
}
Ok(HeaderParseResult { style_store: store, begin_num })
}
fn convert_numbering(hx: &HxNumbering) -> hwpforge_core::NumberingDef {
let levels = hx
.para_heads
.iter()
.map(|ph| hwpforge_core::ParaHead {
start: ph.start,
level: ph.level,
num_format: parse_number_format(&ph.num_format),
text: ph.text.clone(),
checkable: ph.checkable != 0,
})
.collect();
hwpforge_core::NumberingDef { id: hx.id, start: hx.start, levels }
}
pub(crate) fn parse_number_format(s: &str) -> hwpforge_foundation::NumberFormatType {
use hwpforge_foundation::NumberFormatType;
match s {
"DIGIT" => NumberFormatType::Digit,
"CIRCLED_DIGIT" => NumberFormatType::CircledDigit,
"ROMAN_CAPITAL" => NumberFormatType::RomanCapital,
"ROMAN_SMALL" => NumberFormatType::RomanSmall,
"LATIN_CAPITAL" => NumberFormatType::LatinCapital,
"LATIN_SMALL" => NumberFormatType::LatinSmall,
"HANGUL_SYLLABLE" => NumberFormatType::HangulSyllable,
"HANGUL_JAMO" => NumberFormatType::HangulJamo,
"HANJA_DIGIT" => NumberFormatType::HanjaDigit,
"CIRCLED_HANGUL_SYLLABLE" => NumberFormatType::CircledHangulSyllable,
_ => NumberFormatType::Digit,
}
}
fn convert_tab(hx: &HxTabPr) -> hwpforge_core::TabDef {
hwpforge_core::TabDef {
id: hx.id,
auto_tab_left: hx.auto_tab_left != 0,
auto_tab_right: hx.auto_tab_right != 0,
}
}
fn parse_underline_type(s: &str) -> UnderlineType {
match s.to_ascii_uppercase().as_str() {
"NONE" => UnderlineType::None,
"BOTTOM" => UnderlineType::Bottom,
"CENTER" => UnderlineType::Center,
"TOP" => UnderlineType::Top,
_ => UnderlineType::None,
}
}
fn parse_strikeout_shape(s: &str) -> StrikeoutShape {
match s.to_ascii_uppercase().as_str() {
"NONE" => StrikeoutShape::None,
"SLASH" => StrikeoutShape::Continuous,
"DASH" => StrikeoutShape::Dash,
"DOT" => StrikeoutShape::Dot,
"DASH_DOT" => StrikeoutShape::DashDot,
"DASH_DOT_DOT" => StrikeoutShape::DashDotDot,
_ => StrikeoutShape::None,
}
}
fn parse_line_spacing_type(s: &str) -> LineSpacingType {
match s.to_ascii_uppercase().as_str() {
"PERCENT" => LineSpacingType::Percentage,
"FIXED" => LineSpacingType::Fixed,
"BETWEEN_LINES" => LineSpacingType::BetweenLines,
_ => LineSpacingType::Percentage,
}
}
fn parse_outline_type(s: &str) -> OutlineType {
match s.to_ascii_uppercase().as_str() {
"NONE" => OutlineType::None,
"SOLID" => OutlineType::Solid,
_ => OutlineType::None,
}
}
fn parse_shadow_type(s: &str) -> ShadowType {
match s.to_ascii_uppercase().as_str() {
"NONE" => ShadowType::None,
"DROP" => ShadowType::Drop,
_ => ShadowType::None,
}
}
fn parse_optional_hex_color(s: &str) -> Option<Color> {
let s = s.trim();
if s.is_empty() || s.eq_ignore_ascii_case("none") {
return None;
}
let color = parse_hex_color(s);
if color == Color::BLACK {
None
} else {
Some(color)
}
}
fn convert_char_pr(cp: &HxCharPr) -> HwpxCharShape {
let font_ref = cp
.font_ref
.as_ref()
.map(|fr| HwpxFontRef {
hangul: FontIndex::new(fr.hangul as usize),
latin: FontIndex::new(fr.latin as usize),
hanja: FontIndex::new(fr.hanja as usize),
japanese: FontIndex::new(fr.japanese as usize),
other: FontIndex::new(fr.other as usize),
symbol: FontIndex::new(fr.symbol as usize),
user: FontIndex::new(fr.user as usize),
})
.unwrap_or_default();
let height =
i32::try_from(cp.height).ok().and_then(|h| HwpUnit::new(h).ok()).unwrap_or(HwpUnit::ZERO);
HwpxCharShape {
font_ref,
height,
text_color: parse_hex_color(&cp.text_color),
shade_color: parse_optional_hex_color(&cp.shade_color),
bold: cp.bold.is_some(),
italic: cp.italic.is_some(),
underline_type: cp
.underline
.as_ref()
.map(|u| parse_underline_type(&u.underline_type))
.unwrap_or(UnderlineType::None),
underline_color: cp.underline.as_ref().and_then(|u| {
let c = parse_hex_color(&u.color);
if c == Color::BLACK {
None
} else {
Some(c)
}
}),
strikeout_shape: cp
.strikeout
.as_ref()
.map(|s| parse_strikeout_shape(&s.shape))
.unwrap_or(StrikeoutShape::None),
strikeout_color: cp.strikeout.as_ref().and_then(|s| {
let c = parse_hex_color(&s.color);
if c == Color::BLACK {
None
} else {
Some(c)
}
}),
vertical_position: VerticalPosition::Normal, outline_type: cp
.outline
.as_ref()
.map(|o| parse_outline_type(&o.outline_type))
.unwrap_or(OutlineType::None),
shadow_type: cp
.shadow
.as_ref()
.map(|s| parse_shadow_type(&s.shadow_type))
.unwrap_or(ShadowType::None),
emboss_type: EmbossType::None, engrave_type: EngraveType::None, emphasis: parse_emphasis_type(&cp.sym_mark),
ratio: cp.ratio.as_ref().map_or(100, |r| r.hangul),
spacing: cp.spacing.as_ref().map_or(0, |s| s.hangul),
rel_sz: cp.rel_sz.as_ref().map_or(100, |r| r.hangul),
char_offset: cp.offset.as_ref().map_or(0, |o| o.hangul),
use_kerning: cp.use_kerning != 0,
use_font_space: cp.use_font_space != 0,
border_fill_id: if cp.border_fill_id_ref == 2 { None } else { Some(cp.border_fill_id_ref) },
}
}
fn parse_emphasis_type(s: &str) -> EmphasisType {
match s.to_ascii_uppercase().as_str() {
"NONE" => EmphasisType::None,
"DOT_ABOVE" => EmphasisType::DotAbove,
"RING_ABOVE" => EmphasisType::RingAbove,
"TILDE" => EmphasisType::Tilde,
"CARON" => EmphasisType::Caron,
"SIDE" => EmphasisType::Side,
"COLON" => EmphasisType::Colon,
"GRAVE_ACCENT" => EmphasisType::GraveAccent,
"ACUTE_ACCENT" => EmphasisType::AcuteAccent,
"CIRCUMFLEX" => EmphasisType::Circumflex,
"MACRON" => EmphasisType::Macron,
"HOOK_ABOVE" => EmphasisType::HookAbove,
"DOT_BELOW" => EmphasisType::DotBelow,
_ => EmphasisType::None,
}
}
fn convert_para_pr(pp: &HxParaPr) -> HwpxParaShape {
let alignment = pp
.align
.as_ref()
.map(|a| parse_alignment(&a.horizontal))
.unwrap_or(hwpforge_foundation::Alignment::Left);
let (margin_left, margin_right, indent, spacing_before, spacing_after) = extract_margins(pp);
let (line_spacing, line_spacing_type) = extract_line_spacing(pp);
let (break_latin_word, break_non_latin_word) = pp
.break_setting
.as_ref()
.map(|bs| {
(
parse_word_break_type(&bs.break_latin_word),
parse_word_break_type(&bs.break_non_latin_word),
)
})
.unwrap_or_default();
let heading_type = pp
.heading
.as_ref()
.map_or(HeadingType::None, |h| HeadingType::from_hwpx_str(&h.heading_type));
let heading_id_ref = pp.heading.as_ref().map_or(0, |h| h.id_ref);
let heading_level = pp.heading.as_ref().map_or(0, |h| h.level);
let tab_pr_id_ref = pp.tab_pr_id_ref;
let condense = pp.condense;
HwpxParaShape {
alignment,
margin_left,
margin_right,
indent,
spacing_before,
spacing_after,
line_spacing,
line_spacing_type,
break_latin_word,
break_non_latin_word,
heading_type,
heading_id_ref,
heading_level,
tab_pr_id_ref,
condense,
..Default::default()
}
}
fn parse_word_break_type(s: &str) -> WordBreakType {
match s {
"BREAK_WORD" => WordBreakType::BreakWord,
_ => WordBreakType::KeepWord,
}
}
fn convert_style(s: &HxStyle) -> HwpxStyle {
HwpxStyle {
id: s.id,
style_type: s.style_type.clone(),
name: s.name.clone(),
eng_name: s.eng_name.clone(),
para_pr_id_ref: s.para_pr_id_ref,
char_pr_id_ref: s.char_pr_id_ref,
next_style_id_ref: s.next_style_id_ref,
lang_id: s.lang_id,
}
}
fn extract_margins(pp: &HxParaPr) -> (HwpUnit, HwpUnit, HwpUnit, HwpUnit, HwpUnit) {
let z = HwpUnit::ZERO;
let margin = pp.switches.iter().find_map(|sw| sw.default.as_ref()?.margin.as_ref());
let Some(margin) = margin else {
return (z, z, z, z, z);
};
let to_unit = |opt: &Option<crate::schema::header::HxUnitValue>| -> HwpUnit {
opt.as_ref().and_then(|v| HwpUnit::new(v.value).ok()).unwrap_or(z)
};
(
to_unit(&margin.left),
to_unit(&margin.right),
to_unit(&margin.indent),
to_unit(&margin.prev),
to_unit(&margin.next),
)
}
fn extract_line_spacing(pp: &HxParaPr) -> (i32, hwpforge_foundation::LineSpacingType) {
use hwpforge_foundation::LineSpacingType;
let default_ls = (160, LineSpacingType::Percentage);
let ls = pp.switches.iter().find_map(|sw| sw.default.as_ref()?.line_spacing.as_ref());
let Some(ls) = ls else {
return default_ls;
};
let spacing_type = if ls.spacing_type.is_empty() {
LineSpacingType::Percentage
} else {
parse_line_spacing_type(&ls.spacing_type)
};
let value = ls.value.min(i32::MAX as u32) as i32;
(value, spacing_type)
}
fn convert_border_fill(hx: &HxBorderFill) -> HwpxBorderFill {
let fill = hx.fill_brush.as_ref().and_then(|fb| {
fb.win_brush.as_ref().map(|wb| HwpxFill::WinBrush {
face_color: wb.face_color.clone(),
hatch_color: wb.hatch_color.clone(),
alpha: wb.alpha.clone(),
})
});
HwpxBorderFill {
id: hx.id,
three_d: hx.three_d != 0,
shadow: hx.shadow != 0,
center_line: hx.center_line.clone(),
left: convert_border_line(&hx.left_border),
right: convert_border_line(&hx.right_border),
top: convert_border_line(&hx.top_border),
bottom: convert_border_line(&hx.bottom_border),
diagonal: convert_border_line(&hx.diagonal),
slash_type: hx.slash.border_type.clone(),
back_slash_type: hx.back_slash.border_type.clone(),
fill,
}
}
fn convert_border_line(hx: &crate::schema::header::HxBorderLine) -> HwpxBorderLine {
HwpxBorderLine {
line_type: hx.border_type.clone(),
width: hx.width.clone(),
color: hx.color.clone(),
}
}
#[cfg(test)]
mod tests {
use super::*;
use hwpforge_foundation::{Alignment, Color, LineSpacingType, StrikeoutShape, UnderlineType};
#[test]
fn parse_empty_header() {
let xml = r#"<head version="1.4" secCnt="1"></head>"#;
let store = parse_header(xml).unwrap().style_store;
assert_eq!(store.font_count(), 0);
assert_eq!(store.char_shape_count(), 0);
assert_eq!(store.para_shape_count(), 0);
}
#[test]
fn parse_header_with_fonts() {
let xml = r##"<head version="1.4" secCnt="1">
<refList>
<fontfaces itemCnt="2">
<fontface lang="HANGUL" fontCnt="1">
<font id="0" face="함초롬돋움" type="TTF" isEmbedded="0"/>
</fontface>
<fontface lang="LATIN" fontCnt="1">
<font id="0" face="Times New Roman" type="TTF" isEmbedded="0"/>
</fontface>
</fontfaces>
</refList>
</head>"##;
let store = parse_header(xml).unwrap().style_store;
assert_eq!(store.font_count(), 2);
let f0 = store.font(FontIndex::new(0)).unwrap();
assert_eq!(f0.face_name, "함초롬돋움");
assert_eq!(f0.lang, "HANGUL");
let f1 = store.font(FontIndex::new(1)).unwrap();
assert_eq!(f1.face_name, "Times New Roman");
assert_eq!(f1.lang, "LATIN");
}
#[test]
fn parse_char_pr_basic() {
let xml = r##"<head version="1.4" secCnt="1">
<refList>
<charProperties itemCnt="1">
<charPr id="0" height="1000" textColor="#000000" shadeColor="none"
useFontSpace="0" useKerning="0" symMark="NONE" borderFillIDRef="0">
<fontRef hangul="0" latin="0" hanja="0" japanese="0" other="0" symbol="0" user="0"/>
<underline type="NONE" shape="SOLID" color="#000000"/>
<strikeout shape="NONE" color="#000000"/>
<outline type="NONE"/>
<shadow type="NONE" color="#B2B2B2" offsetX="10" offsetY="10"/>
</charPr>
</charProperties>
</refList>
</head>"##;
let store = parse_header(xml).unwrap().style_store;
assert_eq!(store.char_shape_count(), 1);
let cs = store.char_shape(hwpforge_foundation::CharShapeIndex::new(0)).unwrap();
assert_eq!(cs.height.as_i32(), 1000);
assert_eq!(cs.text_color, Color::BLACK);
assert!(!cs.bold);
assert!(!cs.italic);
assert_eq!(cs.underline_type, UnderlineType::None);
assert_eq!(cs.strikeout_shape, StrikeoutShape::None);
}
#[test]
fn parse_char_pr_with_bold_italic_and_color() {
let xml = r##"<head version="1.4" secCnt="1">
<refList>
<charProperties itemCnt="1">
<charPr id="7" height="2500" textColor="#FF0000" shadeColor="#00FF00"
useFontSpace="0" useKerning="0" symMark="NONE" borderFillIDRef="0">
<fontRef hangul="1" latin="2" hanja="0" japanese="0" other="0" symbol="0" user="0"/>
<bold/>
<italic/>
<underline type="BOTTOM" shape="SOLID" color="#000000"/>
<strikeout shape="SLASH" color="#000000"/>
</charPr>
</charProperties>
</refList>
</head>"##;
let store = parse_header(xml).unwrap().style_store;
let cs = store.char_shape(hwpforge_foundation::CharShapeIndex::new(0)).unwrap();
assert_eq!(cs.height.as_i32(), 2500);
assert_eq!(cs.text_color, Color::from_rgb(255, 0, 0));
assert_eq!(cs.shade_color, Some(Color::from_rgb(0, 255, 0)));
assert!(cs.bold);
assert!(cs.italic);
assert_eq!(cs.font_ref.hangul.get(), 1);
assert_eq!(cs.font_ref.latin.get(), 2);
assert_eq!(cs.underline_type, UnderlineType::Bottom);
assert_eq!(cs.strikeout_shape, StrikeoutShape::Continuous);
}
#[test]
fn parse_para_pr_with_alignment() {
let xml = r#"<head version="1.4" secCnt="1">
<refList>
<paraProperties itemCnt="1">
<paraPr id="0">
<align horizontal="CENTER" vertical="BASELINE"/>
</paraPr>
</paraProperties>
</refList>
</head>"#;
let store = parse_header(xml).unwrap().style_store;
assert_eq!(store.para_shape_count(), 1);
let ps = store.para_shape(hwpforge_foundation::ParaShapeIndex::new(0)).unwrap();
assert_eq!(ps.alignment, Alignment::Center);
}
#[test]
fn parse_para_pr_with_switch_margin() {
let xml = r#"<head version="1.4" secCnt="1">
<refList>
<paraProperties itemCnt="1">
<paraPr id="0">
<align horizontal="JUSTIFY" vertical="BASELINE"/>
<switch>
<default>
<margin>
<intent value="200"/>
<left value="100"/>
<right value="50"/>
<prev value="300"/>
<next value="150"/>
</margin>
<lineSpacing type="PERCENT" value="200"/>
</default>
</switch>
</paraPr>
</paraProperties>
</refList>
</head>"#;
let store = parse_header(xml).unwrap().style_store;
let ps = store.para_shape(hwpforge_foundation::ParaShapeIndex::new(0)).unwrap();
assert_eq!(ps.alignment, Alignment::Justify);
assert_eq!(ps.indent.as_i32(), 200);
assert_eq!(ps.margin_left.as_i32(), 100);
assert_eq!(ps.margin_right.as_i32(), 50);
assert_eq!(ps.spacing_before.as_i32(), 300);
assert_eq!(ps.spacing_after.as_i32(), 150);
assert_eq!(ps.line_spacing, 200);
assert_eq!(ps.line_spacing_type, LineSpacingType::Percentage);
}
#[test]
fn parse_para_pr_without_switch_uses_defaults() {
let xml = r#"<head version="1.4" secCnt="1">
<refList>
<paraProperties itemCnt="1">
<paraPr id="0">
<align horizontal="LEFT" vertical="BASELINE"/>
</paraPr>
</paraProperties>
</refList>
</head>"#;
let store = parse_header(xml).unwrap().style_store;
let ps = store.para_shape(hwpforge_foundation::ParaShapeIndex::new(0)).unwrap();
assert_eq!(ps.margin_left, HwpUnit::ZERO);
assert_eq!(ps.line_spacing, 160);
assert_eq!(ps.line_spacing_type, LineSpacingType::Percentage);
}
#[test]
fn parse_full_header_fonts_and_shapes() {
let xml = r##"<head version="1.4" secCnt="1">
<refList>
<fontfaces itemCnt="1">
<fontface lang="HANGUL" fontCnt="2">
<font id="0" face="함초롬돋움" type="TTF" isEmbedded="0"/>
<font id="1" face="함초롬바탕" type="TTF" isEmbedded="0"/>
</fontface>
</fontfaces>
<charProperties itemCnt="2">
<charPr id="0" height="1000" textColor="#000000" shadeColor="none"
useFontSpace="0" useKerning="0" symMark="NONE" borderFillIDRef="0">
<fontRef hangul="0" latin="0" hanja="0" japanese="0" other="0" symbol="0" user="0"/>
</charPr>
<charPr id="1" height="1400" textColor="#0000FF" shadeColor="none"
useFontSpace="0" useKerning="0" symMark="NONE" borderFillIDRef="0">
<fontRef hangul="1" latin="1" hanja="0" japanese="0" other="0" symbol="0" user="0"/>
<bold/>
</charPr>
</charProperties>
<paraProperties itemCnt="1">
<paraPr id="0">
<align horizontal="LEFT" vertical="BASELINE"/>
<switch>
<default>
<margin>
<left value="0"/>
<right value="0"/>
</margin>
<lineSpacing type="PERCENT" value="160"/>
</default>
</switch>
</paraPr>
</paraProperties>
</refList>
</head>"##;
let store = parse_header(xml).unwrap().style_store;
assert_eq!(store.font_count(), 2);
assert_eq!(store.char_shape_count(), 2);
assert_eq!(store.para_shape_count(), 1);
assert_eq!(store.font(FontIndex::new(0)).unwrap().face_name, "함초롬돋움");
assert_eq!(store.font(FontIndex::new(1)).unwrap().face_name, "함초롬바탕");
let cs1 = store.char_shape(hwpforge_foundation::CharShapeIndex::new(1)).unwrap();
assert!(cs1.bold);
assert_eq!(cs1.text_color, Color::from_rgb(0, 0, 255));
assert_eq!(cs1.font_ref.hangul.get(), 1);
}
#[test]
fn parse_invalid_xml() {
let err = parse_header("<not-closed").unwrap_err();
assert!(matches!(err, HwpxError::XmlParse { .. }));
}
#[test]
fn parse_header_with_no_reflist() {
let xml = r#"<head version="1.4" secCnt="1"></head>"#;
let store = parse_header(xml).unwrap().style_store;
assert_eq!(store.font_count(), 0);
}
#[test]
fn char_pr_without_font_ref_gets_default() {
let xml = r##"<head version="1.4" secCnt="1">
<refList>
<charProperties itemCnt="1">
<charPr id="0" height="1000" textColor="#000000" shadeColor="none"
useFontSpace="0" useKerning="0" symMark="NONE" borderFillIDRef="0">
</charPr>
</charProperties>
</refList>
</head>"##;
let store = parse_header(xml).unwrap().style_store;
let cs = store.char_shape(hwpforge_foundation::CharShapeIndex::new(0)).unwrap();
assert_eq!(cs.font_ref.hangul.get(), 0);
assert_eq!(cs.font_ref.latin.get(), 0);
}
#[test]
fn parse_styles_basic() {
let xml = r#"<head version="1.4" secCnt="1">
<refList>
<styles itemCnt="2">
<style id="0" type="PARA" name="바탕글" engName="Normal"
paraPrIDRef="0" charPrIDRef="0" nextStyleIDRef="0" langID="1042"/>
<style id="1" type="CHAR" name="본문" engName="Body"
paraPrIDRef="1" charPrIDRef="1" nextStyleIDRef="1" langID="1042"/>
</styles>
</refList>
</head>"#;
let store = parse_header(xml).unwrap().style_store;
assert_eq!(store.style_count(), 2);
let s0 = store.style(0).unwrap();
assert_eq!(s0.name, "바탕글");
assert_eq!(s0.eng_name, "Normal");
assert_eq!(s0.style_type, "PARA");
assert_eq!(s0.lang_id, 1042);
let s1 = store.style(1).unwrap();
assert_eq!(s1.name, "본문");
assert_eq!(s1.eng_name, "Body");
assert_eq!(s1.style_type, "CHAR");
}
#[test]
fn parse_header_without_styles() {
let xml = r#"<head version="1.4" secCnt="1">
<refList>
<fontfaces itemCnt="1">
<fontface lang="HANGUL" fontCnt="1">
<font id="0" face="함초롬돋움" type="TTF" isEmbedded="0"/>
</fontface>
</fontfaces>
</refList>
</head>"#;
let store = parse_header(xml).unwrap().style_store;
assert_eq!(store.style_count(), 0);
}
}