HotPDF יכולה להחזיק תת-קבוצות גופנים TrueType ו-OpenType על דיסק ולעשות בהן שימוש חוזר בין מסמכים ובין הרצות תהליך, כך שאצווה שמרנדרת עשרת אלפים דפי-חשבון עם אותם שלושה גופנים מבצעת subsetting לאותם גופנים פעם אחת במקום עשרת אלפים פעם. המטמון מוגדר עם שני מאפיינים, נבדק עם רשומה אחת, ובטוח להשאירו דלוק: כשל מטמון נופל חזרה ל-subsetting רגיל בזיכרון ולעולם אינו עוצר מסמך מייצור
Subsetting יקר מסיבה. בניית תת-קבוצה פירושה ללכת על סגירת הגליף, לכתוב מחדש loca ו-glyf, לבנות מחדש cmap ו-hmtx, ולפלוט מיפוי CID שה-PDF יכול לכתוב אליו. עבור מסמך אחד העלות הזו נעלמת לתוך הרעש. עבור שרת דוחות שמייצר מסמכים בלולאה, זוהי לעיתים קרובות בלוק ה-CPU הגדול ביותר בהרצה
מה הופך פגיעה במטמון לאפשרית
ארבעה דברים חייבים להתאים: תוכן הגופן, סט הגליפים בשימוש, מצב ה-subsetting, וסכמת המטמון. החטאת אחד ו-HotPDF מבצעת subsetting מאפס, מפני שתת-קבוצה ניתנת לשימוש חוזר רק כשהייתה ממילא זהה-בבייטים
סט הגליפים הוא התנאי שמפתיע אנשים. שתי חשבוניות שנבדלות בשם לקוח יחיד משתמשות בסטי גליפים שונים, ולכן מייצרות תת-קבוצות שונות ורשומות מטמון שונות. המטמון משתלם כשמסמכים חולקים רפרטואר גליפים — דפי-חשבון מתבנית קבועה, טפסים שהנתונים המשתנים שלהם מספריים, קטלוגים שנשלפים ממסד-נתוני מוצר אחד — ואינו משתלם כשכל מסמך מצייר פרוסה שונה של גופן 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 סופר רשומות שלא ניתן היה לכתוב כלל, מה שבדרך כלל אומר בעיית הרשאות על התיקייה ולא דבר על גופנים. אף אחד משלושת אלה אינו עוצר ייצור מסמך, שזה בדיוק למה צריך להסתכל עליהם: מטמון שלעולם לא כותב בשקט נראה זהה מבחוץ למטמון שעובד, למעט חשבון ה-CPU
פינוי, תקציבים והרגע שבו מקטינים אחד
FontSubsetCacheMaxBytes ברירת המחדל היא 268435456 בייטים, כלומר 256 MiB, וניתן להקטינה בזמן ריצה. הקטנה מפעילה פינוי least-recently-used מיידי במקום לחכות לכתיבה הבאה, כך ששירות שמגיב ללחץ דיסק יכול לשחרר מקום ברגע שהוא מחליט על כך, לא בנקודה מאוחרת כלשהי שאינו שולט בה
הגדרת FontSubsetCacheFolder למחרוזת ריקה משביתה את שכבת הדיסק מבלי לנקות דבר שכבר מאוחסן, ומבלי לשנות בייט אחד של פלט הגופן. זה המאפיין שיש להגיע אליו כשרוצים לבודד את המטמון בזמן אבחון תקלות: כבה אותו, הרץ את אותה אצווה, והשווה את קובצי ה-PDF שיוצרו. הם אמורים להיות זהים, מפני שהמטמון מאחסן תוצאה, לא מדיניות
מה המטמון עושה כשרשומה פגומה
הוא מסיר אותה ומבצע subsetting כרגיל. רשומות מעוותות או קטומות נדחות לפני שהתת-קבוצה יכולה להגיע לזרם 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');
היכן למקם את התיקייה בפריסה אמיתית
שלושה מאפיינים מכריעים זאת: התיקייה חייבת להיות ברת-כתיבה על ידי החשבון שהשירות רץ תחתיו, היא צריכה לשבת על אחסון מקומי במקום שיתוף רשת, ואסור לה להיות בתוך ספרייה שצעד פריסה מוחק. מטמון על שיתוף הופך כל החטאה לנסיעה הלוך וכל פגיעה לשתיים; מטמון תחת תיקיית יישום שה-installer מייצר מחדש הוא מטמון שמתחיל קר אחרי כל עדכון
עבור שירותים מרובי-מופעים, יש לתת לכל מופע תיקייה משלו אלא אם אישרת שהאחסון מטפל בהחלפה אטומית מקבילה כפי שאתה מצפה. העלות של רשומה משוכפלת היא מעבר subsetting אחד נוסף; העלות של ניפוי תחרות מטמון-משותף היא אחר-צהריים
מתי להגיע למשהו אחר
המטמון מפחית עבודה חוזרת. הוא אינו מפחית את עבודת המסמך הראשון, ואינו עוזר לעומס עבודה שסטי הגליפים שלו לעולם אינם חוזרים. אם הפלט שלך נשלט על ידי גופן CJK עצום אחד המשמש בטקסט בלתי-צפוי, המנוף האפקטיבי יותר הוא סגירת ה-subsetting עצמה — אילו גליפים נמשכים פנימה, ולמה — מכוסה בהערות על סגירת תת-קבוצת גופן ועיצוב גליפים. אם האצווה שלך איטית מסיבות שמתבררות כלא קשורות לגופנים כלל, הסקירה של פלט דוחות עם גופנים ותמונות מראה היכן הזמן האחר הולך בדרך כלל, והמקרה-בוחן על באג סדר תת-קבוצת הגופן של EndDoc הוא תזכורת שנכונות subsetting ומהירות subsetting הן בעיות נפרדות
HotPDF היא רכיב PDF VCL מקורי לדלפי ו-C++Builder, ומטמון תת-הקבוצה הוא חלק מהספרייה במקום שירות תוסף, כך ששרת דוחות מקבל אותו על ידי הגדרת נתיב תיקייה אחד — ראה את דף רכיב HotPDF לרשימת התכונות המלאה של גופנים וביצועים