يبدو ملف PDF أفقيًا، لكن الإحداثيات المستخرجة خاطئة: اشرح مصفوفة التدوير بوضوح

قم بتحليل مشكلة اختلال الإحداثيات لصفحات PDF التي تم تدويرها، واشرح بالتفصيل تحويل المصفوفة بين المساحة غير المُدارة ومساحة العرض في PyMuPDF. يوفر أمثلة على تركيب الذاكرة وقوائم مراجعة القبول لشرح كيفية استخدام OmniDoc لتقييم تأثيرات استعادة التخطيط المعقدة في سير عمل ترجمة المستندات.

عندما كنت أقوم بمعالجة ملف PDF، رأيت بوضوح أن الجدول كان على الجانب الأيمن من الصفحة، لكن الإحداثيات المستخرجة تشير إلى الزاوية اليسرى العليا، أو أن كتلة الألوان المميزة سقطت في موضع الرأس. غالبًا ما لا يكون هذا النوع من المشكلات خطأً في مكتبة التحليل، ولكنه يمثل عدم تناسق بين نظام الإحداثيات الخاص بملف PDF والعرض المرئي. هذه المقالة مخصصة للمطورين الذين يحتاجون إلى تحديد موضع النص أو مطابقة المنطقة أو رسم التعليقات التوضيحية. وهو يشرح كيف تتسبب سمة /Rotate في اختلال الإحداثيات، وتستخدم مصفوفة التدوير الخاصة بـ PyMuPDF لإكمال التحويل.

يتم تجميع كافة الأمثلة الموجودة في هذه المقالة في الذاكرة ولا تتم قراءة أي ملفات حقيقية. قبل معالجة المستندات المهمة، يرجى الاحتفاظ بالنسخ الأصلية ثم مراجعتها واحدًا تلو الآخر وفقًا للحدود وبنود القبول في المستند.

ماذا علي أن أفعل إذا كانت إحداثيات ملف PDF غير متطابقة بعد التدوير؟ استنتاج جملة واحدة

استخدم Page.rotation_matrix لتحويل الإحداثيات غير المدورة لعرض الإحداثيات، وتحقق من صفحة page.rotation بصفحة قبل المعالجة. لا تفترض أن الاتجاهات الإحداثية لجميع الصفحات متسقة.

حالتكالممارسات الموصى بهاسبب
إحداثيات النص المستخرج لا تتطابق مع الموضع المرئياضرب الإحداثيات في Page.rotation_matrix قبل التخطيطافتراضيًا، يتم إرجاع إحداثيات النص بالنسبة إلى الصفحة غير المدورة.
تحتاج إلى تعيين موضع النقر مرة أخرى إلى الإحداثيات الأصليةاستخدم Page.derotation_matrix للتحويل العكسيتقوم هذه المصفوفة بإجراء التحويل العكسي من إحداثيات العرض إلى الإحداثيات غير المدورة
البعد الإجمالي للإزاحة مسافة ثابتةتحقق من CropBox وMediaBox، وقم بالتعويض باستخدام page.cropbox_positionسيؤدي عدم الاتساق بين الأصلين إلى إزاحة ثابتة
يتم تدوير الصفحات بزوايا مختلفة في نفس المستنداقرأ page.rotation صفحة تلو الأخرى، ولا تقم بمضاعفة المصفوفة عالميًاقد تكون حالة التدوير للصفحات المختلفة مختلفة
تخطيط معقد (أعمدة متعددة مختلطة ومخططات مدورة جزئيًا)قم بإيقاف العملية التلقائية مؤقتًا وانتقل إلى الحكم اليدويقد يفشل التحقق التلقائي من الصحة في مثل هذه الصفحات

افهم الفرق بين المساحة غير المدارة والمساحة المعروضة

يمكن أن تحمل صفحات PDF سمة /Rotate بقيم 0 أو 90 أو 180 أو 270 درجة. يتم تطبيق هذا التدوير تلقائيًا عندما يعرض القارئ، لكن إحداثيات تدفق محتوى الصفحة لا تزال تعتمد على MediaBox الأصلي غير المدور. إذا استخدمت الإحداثيات الأصلية المستخرجة مباشرةً للرسم أو مطابقة المساحة، فستتم محاذاة النتائج بشكل غير صحيح.

يميز PyMuPDF بوضوح بين نظامي الإحداثيات هذين بدءًا من الإصدار 1.17.0:

  • المساحة غير المُدارة: يتم إرجاع إحداثيات Page.get_text() وPage.get_image_bbox() وجميع كائنات التعليقات التوضيحية بالنسبة إلى الصفحة غير المُدارة بشكل افتراضي.
  • مساحة العرض: فقط Page.rect وPage.bound() سوف يعكسان حجم العرض الفعلي بعد التدوير.

يحافظ هذا التصميم على ثبات عملية استخراج المحتوى. حتى لو تم تعديل زاوية دوران الصفحة، فلن تتغير إحداثيات الاستخراج لنفس النص. ولكن عندما تحتاج إلى إعادة الإحداثيات إلى الصفحة، أو محاذاتها مع التفاعلات المرئية مثل نقرات الماوس، يجب عليك إكمال تحويل الإحداثيات يدويًا.

يوفر PyMuPDF مصفوفتين رئيسيتين:

  1. Page.rotation_matrix: تحويل الإحداثيات غير المدورة لعرض الإحداثيات.

2.Page.derotation_matrix: تنفيذ العملية العكسية.

تقوم هاتان المصفوفتان بتغليف تحويلات الترجمة والتدوير، لذلك لا يتعين على المطورين كتابة حسابات الدوال المثلثية يدويًا.

مثال تركيبي: التوقعات الصحيحة وغير الصحيحة لتحويل الإحداثيات

لتوضيح هذه الآلية، قمنا ببناء مشهد اصطناعي في الذاكرة تمامًا لا يعتمد على ملفات خارجية. إذا كنت بحاجة إلى اختبار مستند حقيقي، فيرجى إعداد ملف PDF باستخدام سمة /Rotate=90 مسبقًا، والتأكد من أن CropBox الخاص به متوافق مع MediaBox للتخلص من تداخل إزاحة الاقتصاص.

إعدادات الإدخال: قم بإنشاء صفحة عمودية بحجم A4 (595 × 842 نقطة)، وقم بتعيين /Rotate = 90. في الفضاء غير المُدار، تقع النقطة (100، 100) بالقرب من الزاوية اليسرى العليا؛ عند تدويرها بمقدار 90 درجة، يتغير الموضع البصري للنقطة بشكل ملحوظ لأن نظام الإحداثيات يدور معها.

الإخراج الصحيح المتوقع: اضرب (100، 100) في rotation_matrix ويجب أن تقع إحداثيات العرض الناتجة ضمن الحدود الصالحة للصفحة التي تم تدويرها (أي page.rect). بالنسبة لصفحة A4 الافتراضية التي يتم تدويرها بمقدار 90 درجة عكس اتجاه عقارب الساعة، يجب أن تكون نقطة الزاوية اليسرى العليا الأصلية موجودة بالقرب من الزاوية اليسرى السفلية في مساحة العرض، أي أن الإحداثي x أكبر والإحداثي y قريب من 0. تعتمد القيمة المحددة على التعريف الأصلي لـ MediaBox. لا تفترض فقط أن y أكبر من x.

معالجة الحالة الحدودية: عندما تكون زاوية الدوران 0، يجب أن تكون rotation_matrix هي مصفوفة الهوية. إذا تم تمرير قيمة دوران ليست من مضاعفات 90 درجة، فسيقوم PyMuPDF عادةً بتطبيعها، ولكن لا يزال يوصى بالتحقق من صحة المصفوفة لتجنب إنشاء إحداثيات NaN.

تنفذ بايثون منطق التحقق الإحداثي

يوضح المثال التالي كيفية التحقق من تحويلات الإحداثيات في الذاكرة. إنه يوضح فقط منطق تشغيل المصفوفة وليس خط أنابيب كاملاً لمعالجة PDF. يرجى التأكد من تثبيت PyMuPDF قبل التشغيل، وتنفيذه في بيئة تم فيها تأمين التبعيات.

import fitz  # PyMuPDF

def validate_rotation_transform():
    """合成示例:验证旋转矩阵在未旋转坐标与显示坐标间的转换"""
    doc = fitz.open()  # 空文档,无需文件路径
    page = doc.new_page(width=595, height=842)

    # 设置页面旋转90度,模拟横向显示的PDF
    page.set_rotation(90)

    # 定义一个在未旋转空间中的测试点 (左上角附近)
    unrotated_point = fitz.Point(100, 100)

    # 获取旋转矩阵并执行变换
    mat = page.rotation_matrix
    display_point = unrotated_point * mat

    # 验证1: 变换后的点必须位于旋转后的页面可视区域内
    assert page.rect.contains(display_point), (
        f"变换后坐标 {display_point} 超出显示边界 {page.rect}"
    )

    # 验证2: 对于默认A4逆时针旋转90度,原(100,100)应变为左下角
    # 即 x 接近页宽(842),y 接近 0。注意:此处纠正了常见的 y>x 误区
    assert display_point.x > 500 and display_point.y < 200, (
        f"90度旋转预期左下角,实际得到 ({display_point.x:.1f}, {display_point.y:.1f})"
    )

    # 验证3: 逆变换可精确恢复原始坐标
    inv_mat = page.derotation_matrix
    recovered = display_point * inv_mat
    assert abs(recovered.x - unrotated_point.x) < 0.01
    assert abs(recovered.y - unrotated_point.y) < 0.01

    doc.close()
    print("坐标变换逻辑校验通过(合成数据)")

# 请在本地环境运行,不要假设线上环境已安装fitz
validate_rotation_transform()

لا يقرأ هذا الرمز أي ملف حقيقي، ولكنه ينشئ حالة الصفحة في الذاكرة. يتحقق بيان assert من عقلانية العلاقة الرياضية، وليس صحة الأعمال. قد تكون هناك حاجة إلى تصحيح أخطاء إضافي إذا كان ملف PDF الفعلي يحتوي على MediaBox غير قياسي أو تحويلات متداخلة.

📊 المقارنة الأساسية

البند المقارنمساحة غير مدارةمساحة العرض
مصادر نموذجيةget_text()، get_image_bbox()، إحداثيات الشرحPage.rect,Page.bound()
مسند التنسيقعرض وارتفاع MediaBox الأصليحجم العرض الفعلي بعد التدوير
الاستخدامات الشائعةاستخراج المحتوى، وتسجيل المنطقة المستقرةارسم التعليقات التوضيحية وقم بمحاذاتها مع موضع النقر
طريقة التحويلاضرب بـ rotation_matrixاضرب بـ derotation_matrix للاستعادة

معايير القبول وحدود المراجعة اليدوية

إن مجرد قدرة الكود على اجتياز التأكيد لا يعني أن بيئة الإنتاج مضمونة. يسرد الجدول التالي الاستثناءات الشائعة وقرارات معالجتها للمساعدة في التمييز بين السيناريوهات التي يمكن تشغيلها تلقائيًا وتتطلب تدخلاً يدويًا.

مراقبة الظواهرالأسباب المحتملةقرار المعالجة
البعد الإجمالي للإزاحة مسافة ثابتةأصول CropBox وMediaBox غير متناسقةاستخدم page.cropbox_position للتعويض عن الإزاحة
بعد التدوير، تكون إحداثيات النص طبيعية ولكن الصورة غير محاذية.تستخدم الصورة مصفوفة تحويل CTM مستقلةاستخدم get_image_rects() بدلاً من get_image_bbox()
يتم تدوير بعض الصفحات بشكل صحيح ويتم تدوير بعضها بشكل غير صحيح.يمزج المستند الصفحات بزوايا دوران مختلفةتحقق من page.rotation صفحة تلو الأخرى، وقم بتعطيل مصفوفة ذاكرة التخزين المؤقت العامة
بعد التحويل، تتجاوز الإحداثيات حدود الصفحة.المحتوى الأصلي نفسه تم اقتصاصه أو له إحداثيات سلبيةوضع علامة على الصفحة كصفحة غير طبيعية ونقلها إلى المراجعة اليدوية
لا تحتاج الصفحة ذات الدرجة 0 إلى التحويل ولكن لا تزال بها انحرافاتبيانات تعريف التدوير المخفية المكتوبة بواسطة أدوات خارجيةفرض استدعاء remove_rotation() لإعادة التعيين ثم المعالجة

يتناول هذا الجدول فقط منطق تحويل الإحداثيات ولا يغطي دقة التعرف الضوئي على الحروف أو مشكلات ترميز الخط. عند مواجهة التنضيد المعقد، قد يفشل التحقق التلقائي. في هذا الوقت، يجب إيقاف التدفق مؤقتًا ويجب اتخاذ قرار يدوي بشأن ما إذا كان سيتم الاستمرار أم لا.

استخدم قوائم المراجعة لترجمة المستندات

قبل الترجمة، تأكد من ترتيب الصفحة وموقع المصدر وسلامة النص، ثم أرسل نفس العينة غير الحساسة إلى عملية الترجمة. بعد الترجمة، لا تزال بحاجة إلى التحقق مما إذا كان العنوان والنص والرسوم التوضيحية متطابقين بشكل صحيح، وليس فقط ما إذا كانت الترجمة سلسة.

توفر OmniDoc (بوابة المنتج) بوابة لترجمة المستندات متعددة التنسيقات، ويقدم الموقع الرسمي وظائف التعرف الضوئي على الحروف (OCR) والمسرد ووظائف المقارنة ثنائية اللغة. بالنسبة للفرق التي ترغب في العمل مباشرة من الملفات، فهذا مسار يستحق التقييم باستخدام عينات من الحياة الواقعية. الرجاء استخدام الملفات غير الحساسة التي لديك إذن بتحميلها، واستخدام عناصر الاختيار في هذه المقالة لمراجعة النتائج. لا يوجد أي ادعاء بأن OmniDoc يستخدم المكتبات مفتوحة المصدر أو الخوارزميات الداخلية المذكورة في هذه المقالة، ولا يحل المثال محل اختبار أداء المنتج.

مزيد من القراءة:

🎯 ما هي الطريقة المناسبة لك؟

إذا كنت بحاجة إلى رسم إحداثيات النص المستخرج مرة أخرى إلى الصفحة

استخدم أولاً rotation_matrix للتحويل إلى عرض الإحداثيات، ثم قم برسم الإبرازات أو التعليقات التوضيحية.

إذا كنت بحاجة إلى إعادة نقرات الماوس إلى المحتوى الأصلي

استخدم derotation_matrix للتحويل العكسي، وتحقق مما إذا كانت النقطة تقع ضمن النطاق الصالح مقابل page.rect.

إذا كنت تتعامل مع مستندات ذات زوايا دوران مختلطة

اقرأ page.rotation صفحة تلو الأخرى، ولا تعيد استخدام المصفوفة العامة، وقم بوضع علامات على الصفحات خارج الحدود.

إذا كانت صفحتك تحتوي على تخطيطات معقدة أو مخططات تم تدويرها جزئيًا

قم أولاً بإجراء فحص للعينات الاصطناعية والعينات الحقيقية صغيرة الحجم، ثم قرر ما إذا كان سيتم تسليمها إلى العملية التلقائية أم لا. عند مواجهة أي تشوهات، قم بتسليمها للمراجعة اليدوية.

❓ الأسئلة الشائعة (الأسئلة الشائعة)

1. لماذا تتعارض الإحداثيات التي يتم إرجاعها بواسطة get_text() مع الموضع البصري؟

نظرًا لأن واجهات برمجة التطبيقات هذه ترجع نسبة إلى الصفحة غير المدورة بشكل افتراضي، يتم تطبيق سمة /Rotate عندما يقوم القارئ بالعرض.

2. هل المسند الإحداثي لـ Page.rect وget_text() هو نفسه؟

لا نفس الشيء. يعكس Page.rect وPage.bound() حجم العرض المستدير، بينما يتم تثبيت إحداثيات واجهات برمجة التطبيقات (APIs) مثل get_text() على العرض والارتفاع الأصليين غير المستديرين.

3. هل لا تزال هناك حاجة للتحويل عندما تكون زاوية الدوران 0؟

غير ضروري. في هذه المرحلة، يجب أن تكون rotation_matrix مصفوفة هوية، ولكن لا يزال من المستحسن التحقق من التحيز لأن أدوات الطرف الثالث قد تكتب بيانات تعريف دوران مخفية.

4. هل يمكن استخدام الكود مباشرة للإنتاج بعد اجتياز التأكيد؟

غير مستحسن. التأكيد يتحقق فقط من عقلانية العلاقات الرياضية. قد يحتوي ملف PDF الفعلي على MediaBox غير قياسي، أو تحويل CTM مستقل أو صفحات غير طبيعية، والتي لا تزال بحاجة إلى المراجعة بندًا بندًا وفقًا لنموذج القبول.

📝 ملخص

يكمن مصدر اختلال الإحداثيات في الفرق بين المساحة غير المدارة ومساحة العرض. باستخدام rotation_matrix وderotation_matrix للتحويل، بالإضافة إلى الفحص كل صفحة على حدة والمراجعة اليدوية، يمكن التحكم في المخاطر ضمن نطاق يمكن التحقق منه.

جربه الآن: قم بتحميل المستند لبدء الترجمة، مع الاحتفاظ بالتخطيط الأصلي

تاريخ التحديث: 2026-11-13