hwp/hwp/doc_info/
bin_data.rs

1use byteorder::{LittleEndian, ReadBytesExt};
2use num::FromPrimitive;
3use num_derive::FromPrimitive;
4
5use crate::hwp::{
6    header::Header,
7    record::{reader::RecordReader, tags::DocInfoRecord, FromRecordCursor, RecordCursor},
8    utils::bits::get_value_range,
9    version::Version,
10};
11
12#[derive(Debug)]
13pub struct BinData {
14    pub properties: BinDataProperties,
15    pub absolute_path: Option<String>,
16    pub relative_path: Option<String>,
17    pub id: Option<u16>,
18    pub extension: Option<String>,
19}
20
21impl BinData {
22    pub fn cfb_file_name(&self) -> Option<String> {
23        if self.properties.kind != BinDataKind::Embedding {
24            return None;
25        }
26
27        let mut extension = self.extension.clone().unwrap();
28        extension.make_ascii_lowercase();
29
30        let id = self.id.unwrap();
31
32        Some(format!("BIN{:0>4X}.{extension}", id))
33    }
34
35    pub fn compressed(&self, header: &Header) -> bool {
36        match self.properties.compress_mode {
37            CompressMode::Default => header.flags.compressed,
38            CompressMode::Compress => true,
39            _ => false,
40        }
41    }
42}
43
44impl FromRecordCursor for BinData {
45    fn from_record_cursor(cursor: &mut RecordCursor, _: &Version) -> Self {
46        let record = cursor.current();
47
48        assert_eq!(
49            record.tag_id,
50            DocInfoRecord::HWPTAG_BIN_DATA as u32,
51            "올바르지 않은 정보"
52        );
53
54        let mut data = record.get_data_reader();
55        let properties = data.read_u16::<LittleEndian>().unwrap();
56        let properties = BinDataProperties::from_bits(properties);
57
58        let absolute_path = if properties.kind == BinDataKind::Link {
59            Some(data.read_string::<LittleEndian>().unwrap())
60        } else {
61            None
62        };
63
64        let relative_path = if properties.kind == BinDataKind::Link {
65            Some(data.read_string::<LittleEndian>().unwrap())
66        } else {
67            None
68        };
69
70        let id = if properties.kind == BinDataKind::Embedding
71            || properties.kind == BinDataKind::Storage
72        {
73            Some(data.read_u16::<LittleEndian>().unwrap())
74        } else {
75            None
76        };
77
78        let extension = if properties.kind == BinDataKind::Embedding {
79            Some(data.read_string::<LittleEndian>().unwrap())
80        } else {
81            None
82        };
83
84        Self {
85            properties,
86            absolute_path,
87            relative_path,
88            id,
89            extension,
90        }
91    }
92}
93
94#[repr(u8)]
95#[derive(Debug, PartialEq, Eq, FromPrimitive)]
96pub enum BinDataKind {
97    /// 그림 외부 파일 참조
98    Link,
99    /// 그림 파일 포함
100    Embedding,
101    /// OLE 포함
102    Storage,
103}
104
105#[repr(u8)]
106#[derive(Debug, PartialEq, Eq, FromPrimitive)]
107pub enum CompressMode {
108    /// 스토리지의 디폴트 모드 따라감
109    Default,
110    /// 무조건 압축
111    Compress,
112    /// 무조건 압축하지 않음
113    None,
114}
115
116#[repr(u8)]
117#[derive(Debug, PartialEq, Eq, FromPrimitive)]
118pub enum BinDataStatus {
119    /// 아직 access 된 적이 없는 상태
120    Initial,
121    /// access에 성공하여 파일을 찾은 상태
122    Success,
123    /// access가 실패한 에러 상태
124    Failed,
125    /// 링크 access가 실패했으나 무시된 상태
126    Ignored,
127}
128
129#[derive(Debug)]
130pub struct BinDataProperties {
131    /// 타입
132    pub kind: BinDataKind,
133    /// 압축 모드
134    pub compress_mode: CompressMode,
135    /// 상태
136    pub status: BinDataStatus,
137}
138
139impl BinDataProperties {
140    pub fn from_bits(bits: u16) -> Self {
141        Self {
142            kind: BinDataKind::from_u16(get_value_range(bits, 0, 3)).unwrap(),
143            compress_mode: CompressMode::from_u16(get_value_range(bits, 4, 5)).unwrap(),
144            status: BinDataStatus::from_u16(get_value_range(bits, 8, 9)).unwrap(),
145        }
146    }
147}