مقال تقني

تخزين المجموعات الجزئية للخطوط على القرص مع HotPDF في Delphi

يستطيع HotPDF الاحتفاظ بالمجموعات الجزئية لخطوط TrueType وOpenType على القرص وإعادة استخدامها عبر المستندات وعبر تشغيلات العملية، فيحدد دفعة تعرض عشرة آلاف كشف بنفس الخطوط الثلاثة تلك الخطوط مرة واحدة بدلًا من عشرة آلاف مرة. تُهيَّأ ذاكرة المؤقتة بخاصيتين، وتُفحَص بسجل واحد، وآمنة تركها مفعّلة: فشل الذاكرة المؤقتة يعود إلى التحديد الفرعي الطبيعي في الذاكرة ولا يوقف إنتاج مستند أبدًا

التحديد الفرعي مكلف لسبب. بناء مجموعة جزئية يعني السير عبر غلاف الرموز، وإعادة كتابة loca وglyf، وإعادة بناء cmap وhmtx، وإصدار تعيين CID يستطيع PDF معالجته. لمستند واحد تختفي تلك التكلفة في الضجيج. لخادم تقارير يُنتج المستندات في حلقة، هي غالبًا أكبر كتلة وحيدة لوقت المعالج في التشغيل

ما الذي يجعل إصابة الذاكرة المؤقتة ممكنة

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

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

var
  Pdf: THotPDF;
  Info: THPDFFontSubsetCacheInfo;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.EnableFontSubsetting := True;
    Pdf.FontSubsetCacheFolder := 'C:\ProgramData\Reports\fontcache';
    Pdf.FontSubsetCacheMaxBytes := 64 * 1024 * 1024;   // 64 MiB, default is 256
    // ... generate the batch ...
    Info := Pdf.GetFontSubsetCacheInfo;
    LogFmt('subset cache: %d hits, %d misses, %d bytes in %d files',
      [Info.HitCount, Info.MissCount, Info.CurrentBytes, Info.FileCount]);
  finally
    Pdf.Free;
  end;
end;

كيف تعرف أن الذاكرة المؤقتة تفعل أي شيء؟

يُعيد GetFontSubsetCacheInfo تسعة عدّادات، والنسبة بين الأولين تجيب عن السؤال مباشرة. يعطي HitCount وMissCount معدل الإصابة. يُظهر WriteCount وEvictionCount إن كانت المدخلات تبقى حيّة بما يكفي لإعادة استخدامها أم تُطرد بسبب ميزانية صغيرة جدًا. يُبلِّغ CurrentBytes وFileCount عمّا على القرص الآن

الثلاثة الباقون هم الذين يستحق التنبيه عليهم. يعدّ CorruptCount المدخلات التي فشلت في التحقق وأُزيلَت — قليلة بعد إيقاف غير نظيف طبيعية، تيار مستمر يعني التخزين غير موثوق. يعدّ RejectedCount المدخلات المرفوضة قبل الاستخدام. يعدّ WriteFailureCount المدخلات التي تعذّرت كتابتها أصلًا، وهذا يعني غالبًا مشكلة أذونات على المجلد لا أي شيء عن الخطوط. لا أحد من الثلاثة يوقف توليد المستند، وهذا بالضبط سبب وجوب النظر إليها: ذاكرة مؤقتة لا تكتب أبدًا بصمت تبدو من الخارج كذاكرة مؤقتة تعمل، باستثناء فاتورة المعالج

الإزاحة، والميزانيات، ولحظة تقليص أحدها

FontSubsetCacheMaxBytes افتراضاته 268435456 بايت، أي 256 MiB، ويمكن خفضه وقت التشغيل. خفضه يُطلق إزاحة الأقل استخدامًا مؤخرًا فورًا بدلًا من انتظار الكتابة التالية، فيستطيع خدم يتفاعل مع ضغط القرص تحرير مساحة في اللحظة التي يقرر فيها، لا في نقطة لاحقة لا يتحكم فيها

ضبط FontSubsetCacheFolder على سلسلة فارغة يُعطّل طبقة القرص دون مسح أي شيء مخزَّن، ودون تغيير بايت واحد من إخراج الخط. هذه هي الخاصية التي تمدّ لها يدك حين تريد عزل الذاكرة المؤقتة أثناء استكشاف الأخطاء: أطفئها، شغّل الدفعة نفسها، وقارن ملفات PDF المُنتَجة. ينبغي أن تكون متطابقة، لأن الذاكرة المؤقتة تخزّن نتيجة لا سياسة

ماذا تفعل الذاكرة المؤقتة حين يتلف مدخل

تزيله وتحدد طبيعيًا. المدخلات المشوَّهة أو المبتورة تُرفَض قبل أن يصل التحديد الفرعي إلى دفق PDF، وهذا جزء التصميم الأهم: مدخل ذاكرة مؤقتة تالف وصل إلى مستند سيُنتج PDF ببرنامج خط مكسور، وسيظهر ذلك الفشل بعيدًا عن سببه — في عارض، على آلة عميل، بعد أسابيع

الكتابات ذرّية، فلا يرى قارئ قطعًا نصف مكتوب، والانهيار أثناء الكتابة يترك الذاكرة المؤقتة متّسقة لا مسمومة. تحتفظ المدخلات المضغوطة ببيانات إعادة تعيين CID التي تتطلبها قواميس خط PDF/A، فالمجموعة الجزئية المخزَّنة لا تزال مجموعة جزئية مطابقة — الإخراج الأرشيفي لا يضطر لتجاوز الذاكرة المؤقتة ليظل صالحًا

// Reset the disk tier after a font upgrade or a schema change
Pdf.ClearFontSubsetCache;

// Or move it somewhere writable and let the budget apply immediately
Pdf.SetFontSubsetCacheFolder('D:\cache\fonts');

أين تضع المجلد في نشر حقيقي

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

للخدمات متعددة المثيل، أعطِ كل مثيل مجلده الخاص ما لم تؤكد أن التخزين يتعامل مع الاستبدال الذري المتزامن كما تتوقع. تكلفة مدخل مكرر عملية تحديد فرعي واحدة إضافية؛ تكلفة تنقيح سباق ذاكرة مؤقتة مشتركة بعد ظهيرة

متى تمد يدك لشيء آخر

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

HotPDF مكوّن VCL أصلي لـ PDF من أجل Delphi وC++Builder، وذاكرة المؤقتة للتحديد الفرعي جزء من المكتبة لا خدمة إضافية، فيحصل عليها خادم التقارير بضبط مسار مجلد واحد — انظر صفحة مكوّن HotPDF لقائمة ميزات الخط والأداء الكاملة