تقنية

قص صفحة PDF وتدويرها: لماذا تتغير النافذة وتبقى العلامات؟

خريطة صفحة400 في300؛ نافذة من100 إلى300 أفقيا ومن70 إلى230 رأسيا تشمل B، وتبقى A وC خارجها.
مواضع علاماتنا الثلاث وحدود النافذة التي اخترناها.

القص يضبط صندوق العرض؛ تدوير الصفحة يختلف عن تحويل محتوى الصفحة. [1]

الخلاصة السريعة

  • صارت نافذة ملفنا المقصوص200 في160، بدلا من400 في300 في الأصل.
  • في النسخة المدورة تبدل مقاس المعاينة إلى160 في200؛ بقي صندوق القص المسجل نفسه.
  • استعدنا نافذة400 في300 من الملف المقصوص المحفوظ، فعادت العلامات الثلاث للمعاينة.
  • تطابقت بايتات تيار المحتوى في الملفات الأربعة؛ لم تتطابق أبعاد صور المعاينة كلها.
  • الأرقام والعلامات في هذه التجربة مؤلفة، والنتائج تخص ملفاتنا وأداة العرض المذكورة.

ارسم صفحة يمكن تتبعها

أنشأنا صفحة أبعادها 400 × 300، ورسمنا عليها ثلاثة مربعات ملونة مع الحروف A وB وC. مرساة الرسم الأولى عند (40,40)، والثانية عند (180,140)، والثالثة عند (340,240). المربع نفسه يبدأ قبل المرساة بعشر وحدات على المحورين، وطول ضلعه35؛ يضع البرنامج الحرف فوقه.

يعرف MediaBox حدود الصفحة، ويحدد CropBox المنطقة المرئية في فضاء إحداثياتها؛ وrotation دوران العرض، دون تعديل /Contents. [2]

في ملفنا يبدأ الصندوق الكبير عند (0,0) وينتهي عند (400,300). اخترنا للصندوق الصغير الركنين (100,70) و(300,230). لذلك عرضه 300−100=200 وارتفاعه 230−70=160. لم نقرأ300 و230 على أنهما عرض وارتفاع؛ هما إحداثيا الركن الثاني في المثال.

أنشئ الملفات الأربعة

شغل البرنامج التالي في مجلد لا يحتوي pdf-case، مع مكتبتَي reportlab وpypdf. استخدمنا Python 3.12.14 وpypdf 6.10.0. ينشئ الأصل أولا، ثم يفتح الملف المقصوص المحفوظ مرتين لإنشاء فرع الدوران وفرع الاستعادة. كل ناتج له اسم مستقل.

from reportlab.pdfgen import canvas
from pypdf import PdfReader, PdfWriter
from pypdf.generic import RectangleObject
from hashlib import sha256
from pathlib import Path
Path("pdf-case").mkdir()
c=canvas.Canvas("pdf-case/original.pdf",pagesize=(400,300),pageCompression=0)
c.setFont("Helvetica",16)
for label,x,y,color in [("A",40,40,(1,0,0)),
    ("B",180,140,(0,.6,0)),("C",340,240,(0,0,1))]:
    c.setFillColorRGB(*color)
    c.rect(x-10,y-10,35,35,fill=1,stroke=0)
    c.setFillColorRGB(0,0,0);c.drawString(x,y+35,label)
c.showPage();c.save()
def save(source,name,box,angle=0):
    w=PdfWriter();page=w.add_page(PdfReader("pdf-case/"+source).pages[0])
    page.cropbox=RectangleObject(box)
    if angle: page.rotate(angle)
    w.write("pdf-case/"+name)
save("original.pdf","crop.pdf",(100,70,300,230))
save("crop.pdf","rotated.pdf",(100,70,300,230),90)
save("crop.pdf","restored.pdf",(0,0,400,300))
for name in ["original","crop","rotated","restored"]:
    page=PdfReader("pdf-case/"+name+".pdf").pages[0]
    print(name,list(page.cropbox),page.rotation,
        sha256(page.get_contents().get_data()).hexdigest())
[1] [2]

كل سطر يطبع اسم الملف، وحدود نافذته، وقيمة الدوران، وبصمة تيار المحتوى. تطابقت البصمات الأربع في تنفيذنا. أعدنا أيضا استخراج الحروف من الملفات المحفوظة، فظهرت A ثم B ثم C في كل واحد؛ لم نعتمد هذا الاستخراج وحده للحكم على شكل الصفحة.

قارن النافذة قبل الدوران وبعده

حولنا كل ملف إلى صورة باستخدام pdftoppm عند72 نقطة في البوصة، مع الخيار -cropbox. كان مقياس وحدة المستخدم في ملفاتنا1. هذه إعدادات المعاينات التي قسناها، وليست قياسا لورق مطبوع أو لحجم نافذة عارض آخر.

أعطى الأصل صورة 400 × 300، وأعطى القص 200 × 160. بقي الحرف B ومربعه داخل الصورة الثانية، بينما خرج موضعا A وC من حدودها. طابقنا ذلك مع إحداثيات الرسم الأول ومع أبعاد ملفات الصور الفعلية.

الملفحجم CropBoxالدورانمعاينة بالبكسلالحروف المرئية
original400 × 3000400 × 300A B C
crop200 × 1600200 × 160B
rotated200 × 16090160 × 200B
restored400 × 3000400 × 300A B C

في ملف rotated ظل الركنان المسجلان (100,70) و(300,230)، لكن قيمة الدوران صارت90. جاءت المعاينة 160 × 200، ورأينا B مدورا مع مربعه. لا نبدل قيم الركنين في سجلنا كي نجعله يشبه أبعاد الصورة؛ نكتب قيمتي الصندوق والدوران معا.

استعد النافذة من الملف المقصوص

قرأ البرنامج crop.pdf من القرص، وأعاد نافذته إلى (0,0,400,300)، ثم حفظ restored.pdf. لم يرسم مربعات جديدة في هذه الخطوة. في معاينة المستعاد عادت العلامات الثلاث، وعادت الأبعاد 400 × 300. ربطنا هذا الفحص بمصدر الفرع نفسه، حتى لا تصبح صورة الأصل دليلا بديلا عن اختبار الاستعادة.

أربع معاينات تخطيطية بمقياس واحد: الأصل400 في300، القص200 في160، الدوران160 في200، والاستعادة400 في300.
الأشكال بالمقياس نفسه؛ كل اسم يشير إلى ملف مستقل من برنامجنا.

نفذنا ضابطا آخر على الملف المقصوص نفسه: حذفنا خيار -cropbox من استدعاء pdftoppm، فعادت صورة 400 × 300 في إعدادنا. تغيرت طريقة إنتاج المعاينة دون إعادة كتابة ذلك الملف. لذلك سجلنا الخيار إلى جانب نتيجة القياس، ولم ننسب كل صورة مختلفة إلى تغير جديد في بيانات الصفحة.

احتفظ بثلاثة سجلات منفصلة

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

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

المراجع

  1. pypdf: Cropping and Transforming PDFs
  2. pypdf: PageObject
أُعد المثال محليا في13 أكتوبر2026.

أُعدّ هذا المقال بمساعدة الذكاء الاصطناعي وراجعته أدوات ذكاء اصطناعي. لا يُنسب إلى مراجع بشري أو خبير مستقل. منهجية المحتوى.