لا يستطيع القارئ ضعيف البصر تمييز النص الأسود على صفحة بيضاء بالتباين الافتراضي، لذلك يطلب وضعًا مظلمًا. الإجابة الساذجة هي عكس كل بكسل من الصفحة المصيرة. يتم شحنها في غضون أسبوع وتنكسر في اليوم التالي: تعود الصور الفوتوغرافية الممسوحة ضوئيًا وكأنها سلبيات أفلام (film negatives)، وتتحول علامات قلم التمييز الأصفر للقارئ إلى لطخة زرقاء غير مقروءة، ويسأل شخص ما لماذا خرجت المطبوعة سوداء صلبة. تستحق هذه الميزة البناء حقًا ومن السهل حقًا الحصول عليها بشكل نصف صحيح، والفجوة بين النتيجتين هي فكرة واحدة: كل قرار لوني ينتمي إلى نقطة معينة في خط أنابيب التصيير (render pipeline)، والانعكاس (inversion) هو الأداة الخاطئة المطبقة في المرحلة الخاطئة. يستخدم الرمز هنا مكون PDFium، وهو عارض مستند إلى PDFium لـ Delphi و C++Builder و Lazarus، والذي تكشف API التصيير الخاصة به هذه المراحل بشكل منفصل
المرشحات هي حالة عرض، وليست حالة مستند أبدًا
تمنع قاعدة واحدة أسوأ فئة من الأخطاء هنا: وضع القراءة يغير كيفية إنتاج الصورة النقطية أو معالجتها لاحقًا، ولا شيء آخر. تظل بايتات PDF دون مساس، وكل وضع قابل للانعكاس عن طريق إعادة التصيير، ولن يكتب "حفظ" (save) أبدًا المظهر المصفى (filtered appearance) مرة أخرى في الملف. يبدو هذا واضحًا حتى يطبع مراجع قانوني عقدًا تحت مرشح نشط ويرفع النسخة المعكوسة. عند هذه النقطة، يتضح أن سؤال "هل تستخدم الطباعة مظهر المستند نفسه أم مظهر الشاشة؟" يستحق إجابة صريحة في المواصفات الخاصة بك، وليس صدفة من مسار التعليمات البرمجية. احتفظ بإعداد المرشح في حالة العارض، وقم بتطبيقه في وقت التصيير، واجعل كل مسار تصدير يعلن المظهر الذي يستخدمه
تدفع القاعدة عن نفسها مرتين. تأتي قابلية الانعكاس مجانًا، لأن تبديل الأوضاع يعيد التصيير من المصدر غير المتغير: لا توجد كومة تراجع (undo stack) للحفاظ عليها ولا توجد طريقة لتشغيل تغييرات الوضع لتدهور الصفحة. تظل سيناريوهات النوافذ المتعددة متماسكة لنفس السبب. يمكن لزاويتي عرض لمستند واحد تشغيل أوضاع مختلفة، نظرًا لأن كل عرض يمتلك حالة العرض التقديمي الخاصة به بينما يظل كائن المستند مشتركًا
صيّر أولاً، ثم حوّل ثانياً
النمط المدعوم هو معالجة الصورة النقطية بعد التصيير: تنتج RenderPage نقطية الصفحة (page raster)، ثم يقوم مسار التحويل بضبطها. يشحن المكون ثلاثة تحويلات كعمليات نقطية في المكان (in-place bitmap operations)، InvertPdfBitmap، DuotonePdfBitmap، و GrayscalePdfBitmap، مما يجعل تبديل الوضع وظيفة نظيفة من مرحلتين:
function TViewerForm.RenderWithMode(W, H: Integer): TBitmap;
begin
Result := Pdf.RenderPage(0, 0, W, H, ro0, [reAnnotations]);
case FReadingMode of
rmInverted: InvertPdfBitmap(Result);
rmHighContrast: DuotonePdfBitmap(Result, clBlack, $0000C8FF); // dark bg, amber text
rmGrayscale: GrayscalePdfBitmap(Result);
end;
// rmNormal falls through: the document keeps its own colors
end;
يتبع هذا التصميم شيئان. أولاً، تتناسب تكلفة التحويل مع حجم الصورة النقطية، وبالتالي ينتمي العمل إلى أي مكان يتم فيه تخزين نتائج التصيير مؤقتًا: قم بتصفية الصورة النقطية المخبأة مرة واحدة، وليس في كل طلاء. ثانياً، نظراً لأن التحويل يتم على الخط النقطي النهائي، فإنه يصيب النص والفن المتجهي (vector art) والصور ومظاهر التعليقات التوضيحية بنفس الطريقة. هذا التوحيد هو بالضبط ما يُخطئ الانعكاس العادي في الصور الفوتوغرافية. وهو السبب في أن التحويل ثنائي اللون يجعل افتراضيًا أفضل للمستندات الثقيلة النصية، نظرًا لأنه يعين النصوع (luminance) على منحدر ألوان مُختار من الظلام إلى النور بدلاً من إلغاء التدرجات؛ يبقى الانعكاس متاحًا كخيار صريح للقراء الذين يريدونه. حواف الصورة الرمزية الأكثر حدة هي رافعة منفصلة. يعمل خيار التصيير reNoSmoothText على إيقاف تشغيل مكافحة التعرج (anti-aliasing) للنص في وقت التصيير ويقترن بشكل جيد بوضع التباين العالي في التكبير الكبير
تدرجان رماديان غير متفقين
تتضمن خيارات التصيير reGrayscale، والذي يبدو كاختصار يتجاوز خطوة المعالجة اللاحقة. إنها ليست نفس العملية:
// Engine-level: grayscale applied during rasterization
GrayA := Pdf.RenderPage(0, 0, W, H, ro0, [reGrayscale]);
// Post-process: render in color, convert the finished bitmap
GrayB := Pdf.RenderPage(0, 0, W, H);
GrayscalePdfBitmap(GrayB);
ينطبق خيار مستوى المحرك على المخرجات النقطية لمحتوى الصورة ولكنه لا يصل إلى التعبئات المتجهة (vector fills) أو ألوان النص، وبالتالي فإن الصفحة ذات العناوين الملونة يمكن أن تعود بصور فوتوغرافية رمادية وعناوين زرقاء بعناد. GrayscalePdfBitmap على الصورة النقطية النهائية تحول كل شيء، دون قيد أو شرط. لا يزال خيار التصيير يكسب مكانه عندما تريد صوراً مشبعة مع الحفاظ على لون النص كإشارة، وهو ما يفضله بعض القراء ضعاف البصر على وجه التحديد. ولكن إذا كان المطلب يقرأ "صفحة ذات تدرج رمادي"، فإن المعالجة اللاحقة هي الإصدار الذي يفي به. أياً كان المسار الذي تختاره، ضع كل من نمطي التحميل الزائد لـ RenderPage في اعتبارك. يعود شكل الوظيفة بصورة نقطية يمتلكها المتصل ويجب تحريرها، وهذا مهم بمجرد أن تضاعف المرشحات عدد الصور النقطية المصيرة أثناء الطيران
الخلفيات وعلامات التحديد وفخ PageColor
ليس كل تعديل مريح عبارة عن تحويل. يعد استبدال خلفية الصفحة البيضاء بنغمة دافئة كافيًا في كثير من الأحيان من تلقاء نفسه للقراء الحساسين للوهج، وله خاصية مخصصة. تحمل الخاصية قاعدة نطاق تصطاد الناس:
// Affects the on-screen view only
PdfView.PageColor := $00D9EDF2; // warm paper tone behind page content
// RenderPage output ignores PageColor; pass the color explicitly
Bmp := Pdf.RenderPage(0, 0, W, H, ro0, [], $00D9EDF2);
يغير PageColor ما يعرضه TPdfView، لكن الصور النقطية المنتجة من خلال RenderPage تحافظ على اللون الأبيض الافتراضي ما لم تقل المعلمة Color خلاف ذلك. العرض موثوق: تعرض الشاشة الصفحة الملونة، ويصدر المستخدم أو يطبع، ويعود الإخراج إلى اللون الأبيض. سجل ذلك تحت نفس قرار سياسة التصدير من القسم الأول
تحدد خصائص الألوان المتبقية علامات التراكب: HighlightColor لنتائج البحث، SelectionColor لتحديد نص المستخدم، ReadingWordColor لمؤشر الكلمة المنطوقة. يجب إعادة فحص كل واحد منهم تحت كل مرشح تقدمه. مؤشر القراءة الكهرماني الذي يعمل على الأبيض يتلاشى بعد الانعكاس؛ يختفي الاختيار الأزرق الباهت في خلفية عالية التباين. حافظ على لوحات تراكب لكل وضع بدلاً من مجموعة عالمية واحدة، واختبر المجموعات عن قصد. المرشحات بالإضافة إلى تحويل النص إلى كلام هو تكوين عادي للقراء الذين تخدمهم هذه الميزة، وليس حالة متطرفة (edge case). تتم تغطية آلية التراكب نفسها في مقالة القارئ الذي يمكن الوصول إليه
الأرقام والتحقق وسؤال الطباعة
يحول WCAG 2.1 هذه الميزة إلى شيء يمكنك قياسه. يطلب معيار النجاح 1.4.3 نسبة تباين قدرها 4.5:1 لنص النص (body text)، ويرفعها 1.4.6 إلى 7:1 لتحسين التباين. تحقق موضعيًا من وضع التباين العالي الخاص بك مقابل تلك النسب باستخدام محلل تباين يعمل على الإخراج الفعلي المُصيَّر. النص الموجود فوق الصور والنص في حقول النموذج هما المكان الذي تفشل فيه النسب بهدوء حتى عندما يمر نص النص
تستحق الطباعة قرارها الخاص، والافتراضي الذي يمكن الدفاع عنه هو المظهر الخاص للمستند، مع تقديم "طباعة كما هو معروض" كخيار صريح للمستخدم. تعد الصفحة المطبوعة دليلاً في سير عمل أكثر مما يميل مؤلفو المشاهدين إلى توقعه، والمطبوعة المعكوسة لعقد هي حادثة دعم ذات نكهة قانونية. اقتران آخر يهم للأداء: يضاعف التصيير المُصفى عمل الصورة النقطية عند كل تبديل للوضع، لذلك لا تقم بتطبيق تحويل على كل رسالة طلاء. قم بتخزين الصورة النقطية المصفاة مؤقتًا (cache) وأعد تشغيل التحويل فقط عندما تتغير الصفحة أو التكبير أو الوضع فعليًا. تعيش إستراتيجية التخزين المؤقت التي تجعل هذا الأمر رخيصًا في ذاكرة التخزين المؤقت للتصيير ومقال أداء التكبير/التصيير
شيء واحد يجب تسويته في واجهة المستخدم بدلاً من الكود الخاص بك: أي وضع هو الوضع الافتراضي الصحيح. لا توجد إجابة واحدة، لذا قدم المجموعة ودع القارئ يختار. التباين العالي يناسب القراءة الثقيلة النصية في معظمها، والانعكاس يناسب القراء الذين يريدون ضوءًا على الظلام بشكل خاص، والتدرج الرمادي يقطع ضوضاء اللون، وصبغة الخلفية تتعامل مع حساسية الوهج. استمر في الاختيار لكل مستخدم، واستعده عند بدء التشغيل، واحتفظ بمسار بضغطة مفتاح واحدة إلى الوضع الطبيعي، نظرًا لأن القارئ الذي يهبط في وضع لا يمكنه قراءته يحتاج إلى مخرج سريع
خيارات التصيير والتحويلات النقطية وخصائص لون العرض المستخدمة هنا تُشحن مع مكون PDFium لـ Delphi و C++Builder و Lazarus/FPC، مع مصدر كامل حتى يمكن مراجعة تطبيقات التحويل أو توسيعها