PyMuPDF では同じテキスト行が 10 以上のスパンに分割されますか?これにより、マージする必要があるかどうかが決まります
PyMuPDF は、同じ単語行を 12 以上のスパンに分割します。ほとんどの場合、フォント、フォント サイズ、スタイルが変更されていますが、必ずしもセグメンテーションが変更されているわけではありません。この記事では、スタイルの変更と段落境界を区別する方法を説明し、翻訳中に文が分断されないようにするためのロジックと受諾フォームの結合例を示します。
PyMuPDF の extractDICT() を使用して PDF テキストを抽出すると、返された結果で一貫した文が 7 つ、8 つ、またはそれ以上の span に分割される場合があります。これは通常、解析エラーではなく、PDF の基礎となる構造を正確に反映しています。フォントの切り替え、色の変更、またはローカル スタイルの調整により、レンダリング時にテキスト フローが中断される可能性があります。
この「スタイルのスライス」を「段落の分割」と間違えると、その後のクリーニング、翻訳、または検索で文の分割エラーが発生しやすくなります。この記事では、スタイルの変更と段落境界を区別する方法を説明し、結合ロジックの結合例を示し、文が分断されないようにするための承諾フォームを提供します。
PDF 内のテキスト行は多くのスパンに分割されています。それらは統合されるべきでしょうか?一文の結論
同じ line 内に複数の span があり、y 座標が同じである場合、主にフォント、フォント サイズ、色の切り替えであり、安全に接続できます。段落の境界は、span の数ではなく、block と line の構造に基づく必要があります。
| あなたの状況 | 推奨される実践方法 | 理由 |
|---|---|---|
| 同じライン内の複数のスパンは同じ y 座標を持ちます | 安全なスプライシングspan.text | 主にフォントまたは色の切り替え |
| スパン間に明らかな水平方向のギャップがある | bbox の間隔を確認し、必要に応じてスペースまたはタブを挿入します。 | タブ文字または列区切りの残りの可能性があるため、ルールが必要です |
| 同じ行内のスパンの y 座標の差がフォント サイズの 10% を超えています | マージせずに元のスパン構造を維持します | インライン数式または添字の可能性があります |
| 単一ワードのスパンと bbox は順序付けされていません | 低品質としてマークし、手動または OCR に変換します | 並べ替えとグループ化の信頼できる根拠の欠如 |
| ブロック全体で意味的に連続したテキスト | 状況に応じた判断が必要であり、自動マージは実行されません。 | ページングフラグメントである可能性があります |
まず、Block、Line、Span の階層関係を明確にします。
PyMuPDF の辞書構造
PyMuPDF の辞書出力は PDF のコンテンツ フロー構造に厳密に従っており、外部から内部への階層は次のとおりです。
block: テキストの段落、表のセル、凡例など、ページ上の独立した植字領域。line:block内の視覚的な 1 行のテキスト。span:lineは、同じフォント、サイズ、色、スタイルを持つ連続した文字の内部シーケンスです。
各レイヤーのフィールドの詳細については、PyMuPDF TextPage 公式ドキュメントを参照してください。
スタイルを切り替えると新しいスパンが生成されるのはなぜですか?
テキスト行の途中に太字、斜体、色の変更、またはフォントのフォールバックがある限り (たとえば、中国語の文字に英語や数字が混在している場合)、新しい span が生成されます。これらの span は同じ line に属し、同じベースラインと垂直位置を共有します。
段落境界で注目すべき点
段落の真の終わりは、span の番号ではなく、line リストの終わりと新しい block の始まりによってマークされます。この 2 つを混同すると、実際的な問題が発生する可能性があります。たとえば、「重要 ヒント」が誤って 2 つの別々の文として認識され、翻訳メモリが完全な用語と一致できなくなります。
合成例: 正しい出力とエッジケース
正しい出力
この問題を説明するために、合成シナリオを構築します。入力 PDF に「注意してください: システムは 5 分後に再起動します。」というテキスト行があるとします。このうち、「注意してください:」は太字フォントを使用し、「システムは 5 分後に再起動します」は Song フォントに戻ります。
正しい extractDICT() 出力には、1 つの line オブジェクトの下に 4 つの span が含まれている必要があります。各 span の bbox は垂直方向に高度に重なり、origin の y 座標は基本的に一致しています。これら 4 つのフラグメントが結合されて、完全な意味単位が形成されます。
低品質の入力
一般的なエラー状況は、スキャンされた PDF が OCR されていない、または PDF ジェネレーターが各漢字を別個のテキスト オブジェクトとして書き込む場合です。この場合、extractDICT() は数十の単一単語 span を返す可能性があり、それらの bbox 間に小さなギャップや不規則な重複が存在する可能性があります。
ソートとグループ化の信頼できる根拠がないため、この状況は文字列の連結だけでは解決できません。ページを「低品質の構造化データ」としてマークし、マージを強制するよりも手動校正または専用の OCR プロセスに送信する方が安全です。
幾何学的特徴に基づくスパン マージ ロジック
サンプルコード
次の Python の例は、extractDICT() の出力構造をメモリ内でエミュレートし、安全なピア スパン マージ関数を実装する方法を示しています。 注意: この例では、純粋にメモリから合成データを構築し、実際の PDF ファイルを読み取りません。これは、結合アルゴリズムのロジックを検証するためにのみ使用されます。
実際に使用する場合は、PyMuPDF のバージョンが extractDICT メソッド (1.23.x 以降を推奨)をサポートしていることを確認し、独自のロックされた依存関係環境で実行してください。
# 合成示例:演示同行span合并逻辑,非真实PDF解析结果
# 依赖: PyMuPDF (pymupdf) >= 1.23.0,仅用于类型参考,本例纯内存构造
def merge_spans_in_line(line_dict, y_tolerance=2.0):
"""合并同一line内y坐标相近的span,保留原始顺序"""
if not line_dict.get("spans"):
return ""
spans = sorted(line_dict["spans"], key=lambda s: s["bbox"][0]) # 按x排序
merged_text = []
current_y = spans[0]["origin"][1]
buffer = [spans[0]["text"]]
for span in spans[1:]:
y = span["origin"][1]
if abs(y - current_y) <= y_tolerance:
buffer.append(span["text"])
else:
merged_text.append("".join(buffer))
buffer = [span["text"]]
current_y = y
merged_text.append("".join(buffer))
return "\n".join(merged_text)
# 构造合成line数据,模拟四种字体混排(正常情况)
synthetic_line = {
"spans": [
{"text": "请注意:", "bbox": (100, 200, 160, 220), "origin": (100, 215)},
{"text": "系统将在", "bbox": (160, 200, 220, 220), "origin": (160, 215)},
{"text": "5", "bbox": (220, 200, 230, 220), "origin": (220, 215)},
{"text": "分钟后重启。", "bbox": (230, 200, 310, 220), "origin": (230, 215)}
]
}
result = merge_spans_in_line(synthetic_line)
assert result == "请注意:系统将在5分钟后重启。", f"合并失败: {result}"
# 边界测试:y坐标差异超过容差,应视为不同行片段(即使在同一line dict中也应分隔)
bad_line = {
"spans": [
{"text": "上行", "bbox": (100, 200, 140, 220), "origin": (100, 215)},
{"text": "下行", "bbox": (100, 230, 140, 250), "origin": (100, 245)}
]
}
bad_result = merge_spans_in_line(bad_line, y_tolerance=2.0)
# y差值30 > 2.0,应被分隔为两行
assert bad_result == "上行\n下行", f"异常行未正确分隔: {repr(bad_result)}"
使用前の注意事項
y_toleranceの値 2.0 は、単なる合成例の設定であり、業界標準ではありません。- 実際のプロジェクトでは、この値はドキュメントの平均フォント サイズに基づいて動的に計算できます。
line自体が正しく分割されていれば、通常は追加の y 座標検証は必要ありません。 - この記事の例は、コード レベルのデモンストレーションのみを示しており、すべての実際のファイルや製品の効果を保証するものではありません。
📊 コアの比較
次の表では、いくつかの処理方法と、上記の受け入れルールに基づいて、観察された状況に基づいてそれらが自動マージに適しているかどうかを比較しています。
| 観察された状況 | 加工方法 | 自動マージに適していますか? |
|---|---|---|
| 同じライン内のスパンの y 座標は一貫しています | 安全なスプライス | はい |
| スパン間に明らかな水平方向のギャップがある | 間隔を確認してからスペースまたはタブを挿入してください | 用心深く、ルールが必要 |
| インライン数式、上付き文字、および下付き文字は、y 座標に大きな違いをもたらします | 元のスパン構造を維持する | いいえ |
| 単一ワードのスパンと bbox は順序付けされていません | 低品質としてフラグを立て、手動または OCR に変換します | いいえ |
| ブロック全体で意味的に連続したテキスト | 文脈に基づいた判断 | いいえ、NLP のサポートが必要です |
受け入れ基準と手動レビューの境界
| 現象を観察する | 考えられる理由 | 処理決定 | 自動マージを適用するかどうか |
|---|---|---|---|
| 同じライン内の複数のスパンは同じ y 座標を持ちます | フォント/色の切り替え | 安全なスプライシングspan.text | はい |
| スパン間に明らかな水平方向のギャップがあります (>スペース幅) | タブ文字と列の残余 | Bボックスの間隔を確認し、スペースまたはタブを挿入します | 用心深く、ルールが必要 |
| 同じ行内のスパン y 座標の差 > フォント サイズ 10% | インライン数式、上付き文字および下付き文字 | マージせずに元のスパン構造を維持します | いいえ |
| 単一ワードのスパンと bbox は順序付けされていません | スキャンまたは破損した PDF | 低品質としてマークし、手動または OCR に変換します | いいえ |
| ブロック間のテキストの意味的連続性 | 改ページ | ブロック間のマージには状況に応じた判断が必要です | いいえ、NLP のサポートが必要です |
この表は、コード デモンストレーション レベルでの構造検証のみをカバーしており、エンドツーエンドの効果を言語モデルまたは手動サンプリングと組み合わせる必要があります。 自動マージは、ドメインの専門家による検証がない限り、法的契約書、医療記録などの形式に依存する文書には使用しないでください。
ご使用前に、お客様ご自身で減感テストPDFをご用意いただき、page.get_text("dict", sort=True)を実行してJSONをエクスポートし、各項目をご確認ください。
文書翻訳前のチェックに使用されます
まずページの順序、ソースの場所、テキストの整合性をチェックしてから、同じ感度を下げたサンプルを翻訳プロセスに送信します。翻訳後も、文章が滑らかかどうかだけでなく、タイトル、本文、イラストが正しく対応しているかどうかもチェックする必要があります。
OmniDoc (製品ポータル)はマルチフォーマット文書翻訳ポータルを提供しており、公式 Web サイトでは OCR、用語集、対訳比較機能が導入されています。ファイルから直接作業したいチームにとって、これは実際のサンプルを使用して評価する価値のあるパスです。アップロード許可のある感度を下げたファイルを使用し、この記事のチェック項目を使用して結果を確認してください。この記事では、OmniDoc がオープン ソース ライブラリや内部アルゴリズムを使用しているという主張はなく、この例が製品パフォーマンス テストに代わるものでもありません。
🎯 どの方法があなたに適していますか?
同じ行にテキストを整理したいだけの場合
lineごとにグループ化し、y 座標が一致したらspan.textを安全に接続します。
インライン数式または上付き文字が見つかった場合
- マージせずに元の
span構造を維持します。
単一の単語の範囲が多数あるスキャンまたは PDF を扱っている場合
- 最初に低品質としてマークし、その後手動校正または専用の OCR プロセスに移行します。
ブロック間またはページ間の翻訳を行う場合
- 文脈上の判断に基づいて、自動的に直接マージすることは適切ではありません。
❓ よくある質問 (FAQ)
1. テキスト行は多くのスパンに分割されます。セグメンテーションする必要があるのでしょうか?
必ずしもそうとは限りませんが、主にフォント、フォント サイズ、色の変更が原因で発生します。同じ line 内の span は、同じベースラインと垂直位置を共有します。
2. 段落の最後は何を読めばよいでしょうか?
span の数ではなく、line リストが終了して新しい block が始まるかどうかを確認してください。
3. 文字列の連結を使用して単一単語のスパンの問題を解決できますか?
お勧めしません。このようなケースには並べ替えやグループ化のための信頼できる根拠が欠けているため、低品質としてフラグを立てて手動または OCR プロセスに渡すことがより賢明です。
4. マージ時に y_tolerance をどのくらいに設定する必要がありますか?
この例の 2.0 は、単なる合成例の設定であり、業界標準ではありません。実際のアイテムは、平均フォント サイズに基づいて動的に計算することも、line のグループ化を直接信頼することもできます。
5. 自動合併は法的契約で使用できますか?
ドメインの専門家によって検証されない限り、適切ではありません。
📝 概要
span をマージできるかどうかを判断する鍵は、まず line と block の構造を調べ、次に幾何学的位置を調べることです。数式、スキャンしたドキュメント、またはブロック間のセマンティクスに遭遇した場合は、それらを強制的につなぎ合わせるのではなく、そのまま保持するか、手動で変換することをお勧めします。


