Skip to content
Merged
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
14 changes: 9 additions & 5 deletions mineru/model/flash/pdf/line_merging.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,14 @@ def union(left_index: int, right_index: int) -> None:
if left_root != right_root:
parents[right_root] = left_root

# 只枚举满足必要分类条件的行对,组内仍按原索引递增,保持并查集认领顺序。
compatible_indices: dict[tuple[int, bool, str | None], list[int]] = {}
for index, line in enumerate(lines):
compatible_indices.setdefault((line.angle, line.formula_candidate_only, line.semantic_type), []).append(index)
for left_index, left_line in enumerate(lines):
for right_index in range(left_index + 1, len(lines)):
for right_index in compatible_indices[(left_line.angle, left_line.formula_candidate_only, left_line.semantic_type)]:
if right_index <= left_index:
continue
right_line = lines[right_index]
if _can_merge_same_baseline_pair(
left_line,
Expand Down Expand Up @@ -531,8 +537,6 @@ def _can_merge_same_baseline_pair(
return False
if first.visual_row_id == second.visual_row_id and (first.split_from_row or second.split_from_row):
return False
if _connection_crosses_table(first.bbox, second.bbox, table_bboxes):
return False
first_height = _line_effective_height(first, first_bbox)
second_height = _line_effective_height(second, second_bbox)
has_compatible_dominant_font = not (
Expand All @@ -548,13 +552,13 @@ def _can_merge_same_baseline_pair(
second_bbox,
second_height,
):
return True
return not _connection_crosses_table(first.bbox, second.bbox, table_bboxes)
return _touching_same_baseline_geometry(
first_bbox,
first_height,
second_bbox,
second_height,
)
) and not _connection_crosses_table(first.bbox, second.bbox, table_bboxes)


def _touching_same_baseline_geometry(
Expand Down
17 changes: 11 additions & 6 deletions mineru/model/flash/pdf/native_text.py
Original file line number Diff line number Diff line change
Expand Up @@ -811,23 +811,28 @@ def _merge_native_inline_scripts(

candidates: list[tuple[float, int, int, Literal["prefix", "suffix"]]] = []
detached_candidate_pairs: set[tuple[int, int, Literal["prefix", "suffix"]]] = set()
# 候选生成期间行不会修改,按本轮索引缓存字符统计,避免每对行重算字号中位数。
compact_texts = ["".join(char for char in line.text if not char.isspace()) for line in lines]
reference_markers = [_INLINE_REFERENCE_MARKER_RE.fullmatch(text) is not None for text in compact_texts]
local_bboxes = [_rotate_bbox_to_upright(line.bbox, page_size, line.angle) for line in lines]
canonical_scales = [_native_typographic_scale(line) for line in lines]
for small_index, small in enumerate(lines):
compact_text = "".join(char for char in small.text if not char.isspace())
compact_text = compact_texts[small_index]
if not compact_text:
continue
small_local_bbox = _rotate_bbox_to_upright(small.bbox, page_size, small.angle)
small_local_bbox = local_bboxes[small_index]
for base_index, base in enumerate(lines):
if small_index == base_index or small.angle != base.angle or small.visual_row_id == base.visual_row_id:
continue
if small.effective_height <= 0 or base.effective_height <= 0:
continue
canonical_small_scale = _native_typographic_scale(small)
canonical_base_scale = _native_typographic_scale(base)
canonical_small_scale = canonical_scales[small_index]
canonical_base_scale = canonical_scales[base_index]
legacy_small_scale = max(0.1, small.effective_height)
legacy_base_scale = max(0.1, base.effective_height)
legacy_ratio = legacy_small_scale / legacy_base_scale
use_canonical_reference_scale = (
_INLINE_REFERENCE_MARKER_RE.fullmatch(compact_text) is not None
reference_markers[small_index]
and not 0.35 <= legacy_ratio <= 0.8
and 0.35 <= canonical_small_scale / canonical_base_scale <= 0.8
)
Expand All @@ -838,7 +843,7 @@ def _merge_native_inline_scripts(
continue
if len(compact_text) > 8 and small_local_bbox[2] - small_local_bbox[0] > 3.0 * base_scale:
continue
base_local_bbox = _rotate_bbox_to_upright(base.bbox, page_size, base.angle)
base_local_bbox = local_bboxes[base_index]
vertical_overlap = max(
0.0,
min(small_local_bbox[3], base_local_bbox[3]) - max(small_local_bbox[1], base_local_bbox[1]),
Expand Down
10 changes: 5 additions & 5 deletions mineru/model/flash/pdf/text_styles.py
Original file line number Diff line number Diff line change
Expand Up @@ -1610,14 +1610,14 @@ def _script_line_char_roles(
local_origins,
memberships,
)
strong_structural_roles = _strong_structural_script_roles(
local_chars,
local_tight_bboxes,
local_origins,
)
if bool(getattr(line, "compact_formula_cluster", False)) or (
bool(getattr(line, "restored_inline_cluster", False)) and bool(regions)
):
strong_structural_roles = _strong_structural_script_roles(
local_chars,
local_tight_bboxes,
local_origins,
)
roles = [strong_structural_roles.get(index, "body") for index in range(len(roles))]
for index, char in enumerate(chars):
char_idx = char.get("char_idx")
Expand Down
34 changes: 34 additions & 0 deletions tests/unittest/test_flash_pdf_native_text.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,40 @@ def _line(
}


def test_inline_script_scale_cache_is_linear_and_local_to_each_pass(monkeypatch: pytest.MonkeyPatch) -> None:
"""每轮字号计算随行数线性增长,后续轮次重新读取已变化的字符字号。"""

lines = [
models._LineItem(
text="body",
bbox=(10.0, index * 30.0, 30.0, index * 30.0 + 10.0),
angle=0,
source_index=index,
visual_row_id=index,
effective_height=10.0,
chars=[{"char": "a", "font": {"size": 10.0}}],
)
for index in range(20)
]
observed: list[float] = []
original = native_text._native_typographic_scale

def record_scale(line: models._LineItem) -> float:
"""记录真实字号计算,验证缓存不跨越行变更边界。"""

value = original(line)
observed.append(value)
return value

monkeypatch.setattr(native_text, "_native_typographic_scale", record_scale)
native_text._merge_native_inline_scripts(lines, (100.0, 700.0))
assert observed == [10.0] * 20
lines[0].chars[0]["font"]["size"] = 12.0
observed.clear()
native_text._merge_native_inline_scripts(lines, (100.0, 700.0))
assert observed == [12.0, *([10.0] * 19)]


def test_private_use_decorative_rule_becomes_axis_line() -> None:
"""验证页首宽幅私用区重复字形不再进入文本输出。"""

Expand Down
Loading