Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
260 changes: 179 additions & 81 deletions src/document_core/commands/table_ops.rs

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions src/document_core/commands/text_editing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,7 @@ impl DocumentCore {
let margin_left = para_style.map(|s| s.margin_left).unwrap_or(0.0);
let margin_right = para_style.map(|s| s.margin_right).unwrap_or(0.0);
let final_width = (available_width - margin_left - margin_right).max(0.0);
let final_width_hwp = crate::renderer::px_to_hwpunit(final_width, self.dpi).max(1);

// 가변 참조로 리플로우 실행
match self.document.sections[section_idx]
Expand All @@ -470,6 +471,10 @@ impl DocumentCore {
if let Some(cell) = table.cells.get_mut(cell_idx) {
if let Some(cell_para) = cell.paragraphs.get_mut(cell_para_idx) {
reflow_line_segs(cell_para, final_width, &self.styles, self.dpi);
for line_seg in &mut cell_para.line_segs {
line_seg.column_start = 0;
line_seg.segment_width = final_width_hwp;
}
}
}
}
Expand Down
39 changes: 20 additions & 19 deletions src/document_core/html_table_import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -436,22 +436,29 @@ impl DocumentCore {
let total_width: u32 = col_widths.iter().sum();
let total_height: u32 = row_heights.iter().sum();

// raw_ctrl_data: CommonObjAttr (table.attr 이후 데이터)
// [0..4] vertical_offset, [4..8] horizontal_offset,
// [8..12] width, [12..16] height, [16..20] z_order,
// [20..22] margin.left, [22..24] margin.right,
// [24..26] margin.top, [26..28] margin.bottom,
// [28..32] instance_id, [32..34] desc_len(=0)
// table.attr: 기존 문서의 표와 동일한 패턴 사용
// 0x082A2311 = treat_as_char | vert_rel_to=Para | horz_rel_to=Column |
// allow_overlap | width_criterion | various layout flags
// 정상 HWP 파일의 모든 표에서 사용되는 표준값
let table_attr: u32 = 0x082A2311;

// raw_ctrl_data: CommonObjAttr
// [0..4] attr, [4..8] vertical_offset, [8..12] horizontal_offset,
// [12..16] width, [16..20] height, [20..24] z_order,
// [24..26] margin.left, [26..28] margin.right,
// [28..30] margin.top, [30..32] margin.bottom,
// [32..36] instance_id, [36..38] desc_len(=0)
let outer_margin: i16 = 283; // 바깥 여백 ~1mm
let mut raw_ctrl_data = vec![0u8; 38]; // 32(base) + 2(desc_len) + 4(extra)
raw_ctrl_data[8..12].copy_from_slice(&total_width.to_le_bytes());
raw_ctrl_data[12..16].copy_from_slice(&total_height.to_le_bytes());
raw_ctrl_data[0..4].copy_from_slice(&table_attr.to_le_bytes());
raw_ctrl_data[12..16].copy_from_slice(&total_width.to_le_bytes());
raw_ctrl_data[16..20].copy_from_slice(&total_height.to_le_bytes());
// 바깥 여백 (left, right, top, bottom)
raw_ctrl_data[20..22].copy_from_slice(&outer_margin.to_le_bytes());
raw_ctrl_data[22..24].copy_from_slice(&outer_margin.to_le_bytes());
raw_ctrl_data[24..26].copy_from_slice(&outer_margin.to_le_bytes());
raw_ctrl_data[26..28].copy_from_slice(&outer_margin.to_le_bytes());
// [28..32] instance_id (DIFF-7 수정: 해시 기반 유니크 값 생성)
raw_ctrl_data[28..30].copy_from_slice(&outer_margin.to_le_bytes());
raw_ctrl_data[30..32].copy_from_slice(&outer_margin.to_le_bytes());
// [32..36] instance_id (DIFF-7 수정: 해시 기반 유니크 값 생성)
// 정상 HWP 파일에서는 instance_id가 고유한 비-0 값을 가짐
let instance_id: u32 = {
// 행/열 수, 셀 수, 총 폭/높이를 조합한 간단한 해시
Expand All @@ -464,8 +471,8 @@ impl DocumentCore {
if h == 0 { h = 0x7c154b69; } // 절대 0이 되지 않도록
h
};
raw_ctrl_data[28..32].copy_from_slice(&instance_id.to_le_bytes());
// [32..34] desc_len = 0, [34..38] reserved = 0
raw_ctrl_data[32..36].copy_from_slice(&instance_id.to_le_bytes());
// [36..38] desc_len = 0

// row_sizes: 각 행의 셀 수
let row_sizes: Vec<i16> = (0..row_count)
Expand Down Expand Up @@ -495,12 +502,6 @@ impl DocumentCore {
bottom: if table_padding_pt[3] > 0.01 { (table_padding_pt[3] * 100.0).round() as i16 } else { 141 },
};

// table.attr: 기존 문서의 표와 동일한 패턴 사용
// 0x082A2311 = treat_as_char | vert_rel_to=Para | horz_rel_to=Column |
// allow_overlap | width_criterion | various layout flags
// 정상 HWP 파일의 모든 표에서 사용되는 표준값
let table_attr: u32 = 0x082A2311;

// raw_table_record_attr: 정상 파일 패턴 기반 (DIFF-5 수정)
// bit 1: 셀 분리 금지 (항상 설정), bit 2: repeat_header
// bit 26: 추가 레이아웃 속성
Expand Down
14 changes: 8 additions & 6 deletions src/model/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,17 +239,19 @@ impl Table {

/// raw_ctrl_data 내 CommonObjAttr의 width/height를 재계산하여 갱신한다.
///
/// raw_ctrl_data 레이아웃 (attr 4바이트 이후):
/// [0..4] vertical_offset, [4..8] horizontal_offset,
/// [8..12] width, [12..16] height, ...
/// raw_ctrl_data 레이아웃:
/// [0..4] attr, [4..8] vertical_offset, [8..12] horizontal_offset,
/// [12..16] width, [16..20] height, ...
pub fn update_ctrl_dimensions(&mut self) {
if self.raw_ctrl_data.len() < 16 {
if self.raw_ctrl_data.len() < 20 {
return;
}
let total_width: HwpUnit = self.get_column_widths().iter().sum();
let total_height: HwpUnit = self.get_row_heights().iter().sum();
self.raw_ctrl_data[8..12].copy_from_slice(&total_width.to_le_bytes());
self.raw_ctrl_data[12..16].copy_from_slice(&total_height.to_le_bytes());
self.common.width = total_width;
self.common.height = total_height;
self.raw_ctrl_data[12..16].copy_from_slice(&total_width.to_le_bytes());
self.raw_ctrl_data[16..20].copy_from_slice(&total_height.to_le_bytes());
}

/// 열별 폭을 추출한다 (col_span==1인 셀 기준).
Expand Down
10 changes: 3 additions & 7 deletions src/parser/hwpx/section.rs
Original file line number Diff line number Diff line change
Expand Up @@ -745,14 +745,10 @@ fn parse_table(
buf.clear();
}

// row_sizes 설정 (행별 셀 높이의 최대값)
// row_sizes는 HWP TABLE 레코드의 행별 셀 수이다.
for r in 0..table.row_count {
let max_h = table.cells.iter()
.filter(|c| c.row == r && c.row_span == 1)
.map(|c| c.height as i16)
.max()
.unwrap_or(0);
row_sizes.push(max_h);
let cell_count = table.cells.iter().filter(|c| c.row == r).count() as i16;
row_sizes.push(cell_count);
}
table.row_sizes = row_sizes;

Expand Down
39 changes: 38 additions & 1 deletion src/renderer/composer/line_breaking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,12 @@ pub(crate) fn reflow_line_segs(
) {
// 기존 LineSeg에서 dimension 값 보존 (원본 HWP 호환성 유지)
let seg_width_hwp = px_to_hwpunit(available_width_px, dpi);
let wrap_column_start_hwp = square_wrap_column_start_hwp(para);
let wrap_segment_width_hwp = if wrap_column_start_hwp > 0 {
(seg_width_hwp - wrap_column_start_hwp).max(1)
} else {
seg_width_hwp
};
let orig = para.line_segs.first().cloned();
let has_valid_orig = orig.as_ref().map(|ls| ls.line_height > 0).unwrap_or(false);

Expand All @@ -649,7 +655,8 @@ pub(crate) fn reflow_line_segs(
text_height: text_height_hwp,
baseline_distance: baseline_distance_hwp,
line_spacing: line_spacing_hwp,
segment_width: seg_width_hwp,
column_start: wrap_column_start_hwp,
segment_width: wrap_segment_width_hwp,
tag: if orig_tag != 0 { orig_tag } else { 0x00060000 },
..Default::default()
}
Expand Down Expand Up @@ -737,6 +744,36 @@ pub(crate) fn reflow_line_segs(
para.line_segs = new_line_segs;
}

fn square_wrap_column_start_hwp(para: &Paragraph) -> i32 {
para.controls
.iter()
.filter_map(|control| match control {
crate::model::control::Control::Table(table)
if !table.common.treat_as_char
&& matches!(
table.common.text_wrap,
crate::model::shape::TextWrap::Square
| crate::model::shape::TextWrap::Tight
| crate::model::shape::TextWrap::Through
) =>
{
let width = if table.common.width > 0 {
table.common.width
} else {
table.get_column_widths().iter().sum()
};
Some(
width as i32
+ table.common.margin.left as i32
+ table.common.margin.right as i32,
)
}
_ => None,
})
.max()
.unwrap_or(0)
}

/// 구역 내 문단들의 vertical_pos를 순차적으로 재계산한다.
///
/// `start_para`부터 구역 끝까지 각 문단의 vpos를 이전 문단의 vpos_end 기준으로 재계산.
Expand Down
Loading