13. سجل التغييرات
ما تغيّر في كل إصدار، وما الذي قد يكسره ذلك عندك.
الإصدار ما زال في 0.x، فالقفزة الصغرى يحقّ لها أن تكسر شيئًا. وحين تكسر، يقول
المدخل بالضبط ماذا كُسر وكيف تتكيّف. لا شيء هنا يتغيّر في صمت.
0.19.1
Section titled “0.19.1”- كان بإمكان سكربت أن يُنهي العملية إنهاءً غير طبيعي بدل أن يُرفض. إذ كان
التداخل في المصدر يتحوّل إلى تداخل في مكدّس الاستدعاء، فنحو ألفَي قوس تكفي
لتفيض المكدّس ويموت التشغيل بإشارة SIGABRT — بلا رمز خروج 3 ولا رسالة ولا شيء
يمكن التقاطه. وستة أشكال تفعل ذلك:
((((…))))و!!!!…و----…والمصفوفات المتداخلة والكتل المتداخلة و"#{"#{…}"}". صار المحلّل يعدّ عمقه ويرفض ما تجاوز 128 مستوى، وهو أعمق بكثير من أي سكربت يكتبه إنسان وأدنى بكثير من حدّ المكدّس. وأسوأ ما في الأمر أنه كان يصيب الأمر الذي تشغّله أولًا على سكربت ليس لك: إذ كانpdfl lintيموت بدل أن يبلّغ. - كان بإمكان حزمة أن تسمّي ملفًا خارج المجلد الذي تُثبَّت فيه. كان الفحص
يرفض
..والشرطة المائلة في البداية، لكنه لا يرفضC:/Windows/evil.dllولا\\server\share\x— فكلاهما لا يحتوي على ذلك، وكلاهما مطلق على Windows حيث يؤدي ضمّه إلى مجلد التثبيت إلى إسقاط المجلد نفسه. صار كل جزء من كل مسار يجب أن يكون اسمًا بسيطًا، وفق قواعد لا تتغيّر بتغيّر المنصّة التي تفكّ الحزمة. - كان بإمكان حزمة أن تستنزف الذاكرة أثناء القراءة. إذ كانت المدخلات تُقرأ كاملة إلى الذاكرة قبل الرجوع إلى البيان — بما فيها مدخلات لا يذكرها البيان أصلًا — فصار 299 كيلوبايت من الأصفار المضغوطة يأخذ 380 ميغابايت، وما هو أكبر يأخذ الجهاز. وصار فكّ الحزمة يتوقف عند 64 ميغابايت، وهو أعلى بكثير من أي حزمة حقيقية من سكربتات وجداول.
- كان
--pages 1-20000000يحجز أرقام الصفحات قبل فتح أي مستند — 214 ميغابايت ثم لا يقول شيئًا. وتُرفض الآن النطاقات التي تتجاوز مليون صفحة.
ما ينبغي معرفته
Section titled “ما ينبغي معرفته”- تضع الإصلاحات أعلاه حدودًا حيث لم تكن هناك حدود، فبعض ما كان يُقبل لم يعد
يُقبل: سكربت يتداخل أعمق من 128 مستوى، وحزمة تتمدّد فوق 64 ميغابايت، وحزمة
تسمّي ملفًا فيه
:أو\، ونطاق صفحات يتجاوز المليون. وكلها بعيدة جدًا عمّا يحويه سكربت أو حزمة أو مستند حقيقي — لكن أيًّا منها لم يكن يفشل من قبل، فدُوّنت هنا بدل أن تُكتشف.
0.19.0
Section titled “0.19.0”أُزيلت أوضاع العارض. لم يعد هناك Flip ولا Fade ولا منزلق Blend
ولا منزلق Differences، ولا مفتاحا space وd اللذان كانا يشغّلانها.
والمتأثر هو العارض المولَّد وحده — فلا يتغيّر تقرير ولا رمز خروج ولا خيار، ولا يلحظ شيءٌ يقرأ مخرجات
pdflأي فرق. والألواح الثلاثة تجيب عمّا كانت تلك الأوضاع تخدمه: فالملفان معًا على الشاشة بدل أن يظهر كل منهما بدوره. وإن كنت قد كتبت تعليمات خاصة تذكر وضعًا أو مفتاحًا، فهي بحاجة إلى مراجعة.
- صار عارض
pixelcompareثلاثة ألواح متجاورة — الأصل، والملف الجديد، وكلاهما وفوقهما الفروق ملوّنة — بدل لوح واحد وقائمة أوضاع. ويبقى اللوحان المرجعيان ثابتين بينما يمسح الثالث، فترى بين ماذا وماذا يقطع المسح. - يحمل كل لوح الزوج نفسه من الأشرطة — قائم وأفقي — في الموضع نفسه، وتتحرك الستة معًا. وهي تتبع المؤشّر: لا شيء تمسكه ولا شيء تصوّبه، فالتقاطع حيث تكون الفأرة. في لوح الفروق يقطع الشريطان، فيظهر الملف الجديد يمين القائم وأسفل الأفقي ويكون تقاطعهما ركن ما يُكشف؛ وفي اللوحين الآخرين هما مسطرتان على العمود نفسه والسطر نفسه من الصفحة. ويبدأ الشريط الأفقي من الأعلى، فيبقى القائم مسحًا عاديًا بكامل الارتفاع إلى أن يدخل المؤشّر أحد الألواح.
- تُكبّر عجلة الفأرة الألواح الثلاثة معًا حتى ثمانية أضعاف حول النقطة التي تحت المؤشّر، ويتوقف التصغير عند الصفحة الملائمة. والسحب بأي زر من أزرار الفأرة يحرّك الصفحة نفسها، في الثلاثة دفعة واحدة وبسرعة المؤشّر مهما كان التكبير. وتحافظ الأشرطة على سُمكها عند أي تكبير. ويعيد Reset view التكبير والإزاحة والأشرطة إلى حالتها الأولى ويبقى معطّلًا ما دام لا شيء يُعاد؛ والانتقال إلى صفحة أخرى يفعل الشيء نفسه، فالتكبير يخصّ صفحته.
- تُقاس الألواح على النافذة: تظهر المقارنة كاملة على الشاشة دون تمرير، مهما كان شكل النافذة، ويحافظ كل لوح على نسب الصفحة. وحين يختلف الملفان في حجم صفحة، تُعرض كل واحدة كاملة داخل الإطار المشترك بدل أن تُمطّ.
- ويفتح على الصفحات المختلفة، وAll يعيد البقية. ففي مستند طويل تكون تلك الصفحات هي سبب فتحه.
0.18.0
Section titled “0.18.0”يتغيّر معرّف أي نتيجة من نوع loading. فهو مشتق من الرسالة، والرسالة لم تعد
تحمل الأسطر الجديدة التي كان pdfium يضعها فيها.
لا يتأثر سوى ما ينتج عن مستند تعذّرت قراءته — ولا ينتج أيٌّ من فحوصك مثل هذا. وإن كان لديك خط أساس يعتمد ذلك المعرّف، فسيلزم تسجيله من جديد. وهذا سبب كون هذا الإصدار ثانويًا لا ترقيعيًا: المعرّف هو ما يقوم عليه خط الأساس، فلا يتغيّر في صمت.
- كان
pdfl packيسجّل الملف المتداخل بفاصل المسار الخاص بالجهاز الذي بنى الحزمة — وعلى Windows يكونdata\batches.csv. والحزمة تُبنى على جهاز وتُثبَّت على آخر، فأي شيء غير/يعني حزمة تُثبَّت ثم لا تعثر على ملفاتها، ولا يعثر عليها تحققنا نفسه. أما الحزم المبنية على Linux وmacOS فلم تتأثر قط. - كان
--outputو--output-fileيُتجاهلان عندما يتعذّر قراءة ملف PDF. إذ كانت الأوامرrunوfixوcompareتطبع JSON على stdout مهما كان المطلوب، فخط المعالجة الذي يطلب JUnit في ملف لا يحصل على ملف أصلًا، ويذهب التقرير إلى مجرى لا يقرأه — فيبدو الملف التالف وكأن التشغيل لم يحدث قط، مع أن رمز الخروج كان يقول10بشكل صحيح. صار تقرير الفشل يخرج من المسار نفسه الذي تخرج منه بقية التقارير. - كان خطأ pdfium يصل إلى التقرير على هيئة enum من Rust مطبوع على ثلاثة أسطر. والتشخيص حقلٌ في صف CSV وسمةٌ في XML، فصار يُطوى الآن في سطر واحد.
0.17.0
Section titled “0.17.0”-
يعرض
pdfl pixelcompareشريط تقدّم لكل مرحلة — تحويل كل ملف إلى نقاط، والمقارنة، وكتابة العارض. ولا يُرسم إلا إذا كان stderr طرفية، لأن الشريط يكتب فوق سطره ولا مؤشّر في ملف السجل يتحرّك؛ وعند إعادة التوجيه يصمت. و--quietيُسكته في كل الأحوال. -
الخيار
--jobs <n>فيpdfl pixelcompareيقارن هذا العدد من الصفحات في آنٍ واحد، وقيمته الافتراضية واحدة لكل معالج: 41 صفحة بدقة 150 نقطة/بوصة تنزل من 3.6 ث إلى 1.2 ث. المقارنة وحدها هي المتوازية — إذ يُسلسل pdfium كل استدعاء خلف قفل عام واحد، فالتنقيط لا يمكن أن يتوازى، ولهذا كان المكسب ثلاثة أضعاف لا ثمانية. ولا يتأثر التقرير بالقيمة: تُجمَّع الصفحات بترتيب أرقامها، فتبقى التشخيصات وترتيبها وبصماتها متطابقة، وكذلك ملفات العارض.الافتراضي هنا واحدة لكل معالج، بينما يستخدم
testوwatchالقيمة--jobs 1. فهناك تكون المهمة عمليةً ابنة تحمل مستندها الخاص؛ أما هنا فالصفحات في الذاكرة أصلًا والخيوط تتشاركها.
0.16.0
Section titled “0.16.0”pdfl pixelcompareيقارن ملفَّي PDF بمظهرهما لا بنصّهما، صفحةً صفحة، ويُبلغ عن نسبة البكسلات المختلفة. والصفحة التي انزاحت فحسب تُحاذى قبل المقارنة، كي لا تدفن إزاحةُ بكسل واحد التغييرَ المهم.- و
--viewer <مجلد>فيpixelcompareيكتب تطبيقًا مكتفيًا بذاته — بلا CDN ولا أداة تجميع ولا خادم — للكنس أو التبديل أو المزج بين الملفين، والفروق ملوّنة في مكانها: أحمر للحبر الذي اختفى، وأخضر للجديد، وأزرق للوزن نفسه بلون آخر. ويمكن ترشيح شريط الصفحات إلى Changed only، وتتبع مفاتيح الأسهم الترشيح — ففي مستند طويل يكون المرور على الصفحات التي لم تتغيّر هو الجزء البطيء.
0.15.0
Section titled “0.15.0”ifوelse ifوelseبوصفها تعبيرًا: قيمتها آخر تعبير في الفرع الذي جرى، وهي القاعدة نفسها التي تتبعها الدالة أصلًا. فتصلح قيمةً (const LIMIT = if coated { 300 } else { 260 }) وحارسًا حول الأوامر، بلا صياغة ثانية. والفرع الذي لا يجري يعطيnull، ولكل فرع نطاقه الخاص — أما الإسناد إلى متغير موجود في الخارج فيظل يحدّث ذلك المتغير.
0.14.0
Section titled “0.14.0”- صارت
--varتصل الآن إلىpdfl testوpdfl watch، لا إلىpdfl runفقط. فلم يكن أيٌّ منهما يمرّرها إلى العمليات الابنة التي يُطلقها، فتعذّر اختبار أو مراقبة أي نص برمجي يقرأvars.*أصلًا — إذ كانت كل حالة أو ملف تُخفق بعبارة “was not provided”، أيًّا كان محتواه.
0.13.0
Section titled “0.13.0”- لم يعد
pdfl packيحزم ملفات الجداول (.xlsxو.xlsو.ods)، وهو يسمّي الملف الذي تركه. إذ لا تفتحها أي دالة فيdata::، فالحزمة التي تحملها كانت تُثبَّت بلا مشكلة ثم تخفق عند أول بحث. وإن كنت تحزم جدولًا، فصدّره أولًا إلى.csvأو.json.
--tags TAGفيrunيرشّح أي الفحوص تُنفَّذ. قابل للتكرار؛ يُنفَّذ الفحص إذا حمل أيًّا من الوسوم المذكورة.--jsonفيinspectوlint، و--output jsonفيdoc. كل أمر فرعي صار مقروءًا للبرامج.--output sarifو--output junit، في كل موضع تُختار فيه صيغة تقرير —runوcompareوwatchوfix. فـ SARIF هي ما يقرأه GitHub code scanning، وJUnit هي ما تقرأه لوحة الاختبارات في أي تكامل مستمر.pdfl completions <shell>يطبع نصّ الإكمال التلقائي لـ bash أو zsh أو fish أو elvish أو powershell.--quietفي كل الأوامر يُسكت رسائل التقدّم والتأكيد على stderr. أما الأخطاء فتبقى ظاهرة، وprint()لا يُمسّ — فتلك مخرجات النص البرمجي نفسه، وابتلاعها يغيّر ما يفعله.data::load_datasetوdata::lookup_valueيقرآن.jsonإلى جانب.csv: مصفوفةً من المصفوفات، أو مصفوفةً من الكائنات يسمّي أولُها الأعمدة بالترتيب الذي كُتب في الملف.pdfl test <script>يشغّل نصًّا برمجيًا على مجلد من ملفات PDF ويقارن كل تقرير بالمسجَّل بجانبه، فالملف التعريفي الذي يبدأ بإيجاد شيء مختلف يُسقِط اختبارًا بدل أن يفاجئ أحدًا في آخر السلسلة. و--updateيسجّل التقارير المتوقعة.--jobs <n>فيpdfl testيشغّل ذلك العدد من الحالات معًا، كلٌّ في عمليتها. في ثمانية ملفات من 41 صفحة: 8.9 ثانية عند--jobs 1، و1.1 ثانية عند--jobs 8. والافتراضي يبقى1لأن كل مَهمّة تحمل مستندًا في الذاكرة؛ و--jobs 0يعطي واحدة لكل معالج.- و
--jobs <n>فيpdfl watchأيضًا: تفحص العمليات الابنة الملفات، فتتوسّع جولة الدفعة بالطريقة نفسها (من 9.5 إلى 1.2 ثانية في ثمانية ملفات من 41 صفحة). والتقرير المكتوب واحد مهما كانت قيمة--jobs. --eventsفيpdfl watchينتظر إشعارات نظام الملفات بدل المؤقّت. وهو اختياري لا افتراضي: فـ inotify على وصلة NFS أو SMB لا يبلّغ إلا عمّا يكتبه الجهاز المحلي، فيصمت المجلد الشبكي. وإن تعذّر إنشاء المراقِب قال ذلك وعاد إلى المؤقّت.--journal <ملف>فيpdfl watch: سجلّ بالإضافة فقط لما جرى التحقق منه، كائن JSON لكل ملف. وإعادة التشغيل بالسجلّ نفسه تتخطّى ما يغطّيه — فالدفعة التي انقطعت عند أربعة آلاف من خمسة تُتمّ الألف الباقية — مع الإبلاغ عن أحكامها، فلا تدّعي دفعةٌ مستأنَفة أن المجلد نظيف.--timeout <s>فيpdfl watchيقتل تحليل الملف بعد هذا العدد من الثواني ويُبلغ عنه مرفوضًا — ملاحظة واحدة،check_name: "timeout"— بدل أن يترك الدفعة عالقة. والاستدعاء الذاتي في نص.pdflمحدود العمق أصلًا، فهذا الخيار لما لا يستطيع النص البرمجي التسبّب فيه: أن يدخل pdfium في حلقة أو يتجمّد أمام ملف PDF مشوَّه أو عدائي.
ممّا يحسن معرفته
Section titled “ممّا يحسن معرفته”- وسمٌ لا يحمله أي فحص هو خطأ، لا نجاحًا فارغًا. وإلا لما تحققت خطّ إنتاج كُتب فيها الوسم غلطًا من شيء، ثم أبلغت أن الملف نظيف.
- الـ
ruleلا يحمل وسومًا، فـ--tagsيتخطّاه — وهو الجواب نفسه الذي يناله فحصٌ بلا وسم. - اكتسب تقرير JSON حقل
checks_run، أي الفحوص والقواعد التي جرت. وهو لا يرفعschema_version، لأن من يتجاهل الحقول المجهولة يمرّ به سالمًا. وJUnit يحتاجه: فالتشخيصات لا تسمّي إلا الفحوص التي وجدت شيئًا، والتشغيلة النظيفة إذا رُوِيت بصفر اختبارات قرأها التكامل المستمر تشغيلةً لم تحدث.
- صار
pdfl watchيستيقظ حين يستقرّ أحدث ملف، بدل أن ينتظر دورة كاملة بعده. مع--debounce 3000يُبلَّغ عن الملف الواصل بعد نحو 3 ثوانٍ لا بعد 6.
0.12.0
Section titled “0.12.0”- السكربتات تستقبل قيمًا من سطر الأوامر:
--var الاسم=القيمة، تُقرأ بصيغةvars.الاسم. والقيمة الغائبة تسمّي الخيار الذي كان سيوفّرها، بدل أن تؤول إلى لا شيء. - أربعة أمثلة كاملة لمقارنة ملفّين بـ
visual:::proof.pdflوreprint.pdflوscope.pdflوintake.pdfl.
لا شيء. السكربت الذي لا يذكر vars يسلك تمامًا كما كان.
0.11.0
Section titled “0.11.0”تغيّر شكل معرّفات التشخيص. كانت PDFL-001، أي عدّادًا داخل التشغيلة
الواحدة؛ وصارت تُشتقّ من الملاحظة نفسها، مثل PDFL-093751a2.
كل ما كان يطابق
PDFL-\d+لم يعد يطابق. وفي المقابل، صار المعرّف يبقى على حاله إذا أُدرج فحصٌ فوقه — وهذا وحده ما يجعل خطّ أساس معتمدًا أمرًا ممكنًا.
المدخل غير المقروء يخرج بالرمز 10 بدل 2. فقد كان الملف التالف والملف
المرفوض لا يتمايزان أمام خطّ الإنتاج.
إن كان تكاملك المستمر يعامل
2على أنه «هذا الملف رُفض»، فسيرى10حين لا يكون الملف قد حوكم أصلًا. الملاحظات تبقى على0و1و2؛ وخطأ الصياغة في السكربت يبقى على3.
- يستطيع الفحص أن يعلن شدّة ملاحظاته:
check "..." severity: warning { ... }— إماerror(الافتراضي) أوwarningأوinfo. وبهذا صار لـ--fail-on warningما يعمل عليه. - تقرير JSON يبدأ بـ
schema_version، ليعرف المستهلك أي شكل يقرأ. ولا يرتفع الرقم إلا إذا كان قارئ المخرَج السابق سينكسر؛ وإضافة حقل لا ترفعه.
0.10.1
Section titled “0.10.1”- كان تقرير PDF مكتوبًا بالبرتغالية جزئيًا: عنوان القسم يقول
Diagnósticos، وكل تشخيص يحمل(linha N). صارا بالإنجليزية الآن، وهو ما وعدت به الوثائق دائمًا.
0.10.0
Section titled “0.10.0”أهداف الإصدار تغيّر اسمها من x64 إلى amd64، فتغيّر معها اسم كل أصل.
لم تعد الحِزم المحمولة تُنشر، عدا واحدة للينكس amd64.
كل ما ينزّل
pdfl-<الإصدار>-linux-x64.tar.gz، أو أي حزمة محمولة غير حزمة لينكس amd64، يحتاج إلى تغيير. في التكامل المستمر، ثبّت من ملف.deb— وقد حُدّثت وصفات هذه الوثائق على هذا الأساس — أو خذ حزمة لينكس amd64 حيث يتعذّر التثبيت.
- ثغرتان ظهرتا عند مقابلة الوثائق بالشيفرة:
text::detect_personal_dataوtext::detect_piiتقبلان نصًّا اختياريًا لم يُوثَّق في أي مكان، وfix::reorder_pagesكُتب بصيغتين مختلفتين بين اللغات.
- مثبّتات لكل منصّة:
.debللينكس، و.dmgلماك، و-setup.exeو.msiلويندوز. - بناءات ماك على معالج إنتل، مترجَمة تصالبيًا من منصّة Apple Silicon.
- كان مثبّت ويندوز يُبنى بمسارات تُحلّ من الدليل الخطأ، فلم يكن ينتج ملفًّا قط.
- كانت حِزم الإصدار تحمل ترويسات C وملفات بناء pdfium، وهي لا تعني إلا من يترجم مقابل pdfium. نحو 550 كيلوبايت في كل حزمة.
- ويندوز x64 ينضمّ إلى المنصّات المنشورة.
ملف
pdfium.dllالمرفق يقع فيpdfium\bin، لا فيpdfium\lib. وإن حزمتpdflبنفسك فأبقِ التوزيع كما سُلّم: الملف التنفيذي يبحث عن المكتبة إلى جانبه.
أصول الإصدار تحمل رقم الإصدار في اسمها، بصيغة
pdfl-<الإصدار>-<الهدف>.tar.gz، والدليل داخلها كذلك.
.../releases/latest/download/<الاسم>لم يعد يُحلّ، لأن هذه النقطة تطلب اسم الملف بالضبط. نزّل بنمطٍ بدل ذلك:gh release download --pattern 'pdfl-*-linux-amd64.*'.
- الشيفرة وملف README والأمثلة بالإنجليزية. والوثائق تبقى بسبع لغات.
v0.6.1
Section titled “v0.6.1”أول إصدار عام. اللغة والمفسّر وعشرة أوامر لسطر الأوامر، مع وثائق بسبع لغات.