PDFium Component מאפשר לאפליקציית דלפי להחליט אילו בייטי גופן ישמשו כאשר PDF מפנה לגופן שהוא לא מטמיע. ConfigureSystemFontProvider מתקין מימוש IPdfSystemFontProvider שמקבל כל בקשת מיפוי גופן ש-PDFium יוצרת, כולל שם צורה, משקל, דגל נטוי, ערכת תווים ומשפחת פיץ', ועונה עם בייטי TrueType, אוסף TrueType או OpenType לשימוש
זה קיים מכיוון שגופנים לא-מוטמעים הם הגרלת הצגה. PDF שנוקב Arial ולא מטמיע כלום מוצג עם Arial בתחנת עבודה, עם תחליף תואם-מדדים בשרת לינוקס, ועם מה שממפה המארח מוצא בתמונת קונטיינר נעולה. אותה חשבונית נראית שונה בכל אחד, שבירות שורה זזות, ולקוח מקבל מסמך שלא תואם לעותק המאורכב
למה לא פשוט להתקין את הגופנים על השרת?
לפעמים זו התשובה, וכשכן, קחו אותה. אבל היא נכשלת בשלוש סיטואציות נפוצות. רישוי עשוי לאסור התקנת גופן על שרת עבור הצגה אוטומטית. תמונות קונטיינר נבנות מחדש לעיתים קרובות וגופן שהותקן ידנית נעלם עם הפריסה הבאה. וזרימות עבודה מוסדרות זקוקות לכך שמחסנית ההצגה תהיה ניתנת לשחזור מתוצרים תחת בקרת גרסאות, מה שהתקנת גופן ברמת מכונה אינה
ספק מטפל בכל שלוש על ידי העברת ההחלטה לתוך האפליקציה שלכם. גופנים משוגרים כמשאבים שאתם שולטים בהם, מדיניות המיפוי היא קוד שאתם יכולים לסקור, ואותו קובץ בינארי מציג באופן זהה בכל מקום מכיוון שכלום לא תלוי במה שקורה להיות מותקן
התקנת ספק
ההגדרה חייבת לקרות לפני שהספרייה נטענת. PDFium מקבל מבנה מידע גופן מערכת באתחול ושומר ידיות שהוא מוסר לאחר מכן, כך שהחלפת ספק בזמן שמסמכים פתוחים תבטל ידיות גופן ש-PDFium עדיין מחזיק; הרכיב דוחה זאת לחלוטין במקום לתת לזה להשחית הצגה:
uses
PDFium;
type
TAppFontProvider = class(TInterfacedObject, IPdfSystemFontProvider)
public
function ResolveFont(const Request: TPdfSystemFontRequest;
out Font: TPdfSystemFontData): Boolean;
end;
function TAppFontProvider.ResolveFont(const Request: TPdfSystemFontRequest;
out Font: TPdfSystemFontData): Boolean;
var
Path: string;
begin
// מיפוי דטרמיניסטי: שם הצורה בתוספת המשקל והנטייה מחליטים
// איזה קובץ אנחנו משגרים עבור בקשה זו
Path := MapFaceToBundledFile(Request.FaceName, Request.Weight,
Request.Italic, Request.Charset);
Result := Path <> '';
if not Result then
Exit;
Font.FaceName := Request.FaceName;
Font.FontData := LoadFileBytes(Path); // בייטי sfnt או TTC שלמים
Font.Charset := Request.Charset;
Font.TTCIndex := 0; // אינדקס בתוך אוסף
end;
var
Policy: TPdfSystemFontPolicy;
begin
Policy := TPdfSystemFontPolicy.Default;
Policy.AllowDefaultFallback := False; // המארח מחליט על הכל
Policy.AllowFaceSubstitution := False; // דחיית שם צורה שונה
Policy.MaxFontBytes := 32 * 1024 * 1024;
Policy.MaxCacheEntries := 64;
ConfigureSystemFontProvider(TAppFontProvider.Create, Policy);
// רק עכשיו טענו את הספרייה ופתחו מסמכים
end;
הפירוק רץ בסדר ההפוך: הספק מתנתק מ-PDFium קודם, ואז הספרייה נפרקת. דילוג על הניתוק משאיר ידיות גופן ילידיות מצביעות על אובייקטי Pascal שעומדים להשתחרר, שזוהי חריגת הגישה הקלאסית של כיבוי בקוד שמערבב ממשקים נספרי-הפניות עם ספריית C
מה דגלי המדיניות באמת מחליטים
AllowDefaultFallback הוא המתג בין שני מצבי הפעלה. כשהוא כבוי, בקשה שהספק דוחה פשוט נכשלת, מה שרוצים בזמן הוכחת שכל גופן בקורפוס מכוסה: כל פער נעשה גלוי מיד במקום להיטשטש. כשהוא דלוק, בקשות שלא נפתרו מוקצות למפה שמוחזר על ידי FPDF_GetDefaultSystemFontInfo, בעוד העולם החיצוני עדיין רואה עוטף ידית אחיד, כשם הצורה, ערכת התווים, נתוני הטבלה ומחיקת הגופן מנותבים נכון לפי מקור
AllowFaceSubstitution קובע אם ספק רשאי לענות עם שם צורה שונה מזה שהתבקש. כיבויו הופך החלפה להחלטה מפורשת ולא לתאונה, מה שחשוב כשמסמך נוקב גופן שהמדדים שלו נבדלים מספיק כדי לשנות חלוקה לעמודים
הרכיב מאמת כל תגובת ספק לפני שהיא מגיעה ל-PDFium: נתונים ריקים נדחים, גופנים גדולים מדי נדחים כנגד MaxFontBytes, אינדקס ה-TTC נבדק, וטבלאות sfnt בודדות מוגשות מספריית הגופנים כש-PDFium מבקש טבלה במקום קובץ שלם. היכולת האחרונה הזו אומרת שספק יכול למסור קובץ גופן שלם ולתת לרכיב לענות על שאילתות ברמת טבלה, במקום לחשוף אובייקטי Pascal גולמיים על פני ה-ABI של C
מטמון בלי נתוני גופן תלויים
בקשות מיפוי גופן חוזרות ללא הרף במהלך הצגה, כך שתגובות מומטמנות עם מפתח המכסה כל פרמטר בחירת גופן, מפונות בסדר שימוש-אחרון-מוגבל. הדקות היא אורך חיים: PDFium עשוי עדיין לקרוא את הבייטים של גופן שרשומת המטמון שלו זה עתה פונתה
המטמון שומר מערכים דינמיים נספרי-הפניות וכל ידית ילידית מחזיקה תמונת מצב משלה, כך שפינוי מפיל הפניה במקום לשחרר זיכרון בשימוש. קריאת המחיקה משחררת את הידית ומתחזקת מונה פעיל. באופן מעשי, זה אומר ש-MaxCacheEntries ניתן לכוונון עבור זיכרון בלי שום סיכון למשיכת נתונים מתחת להצגה שרצה כרגע
האם הספק נקרא על השרשור שלי?
לא, לא בהכרח. PDFium עשוי לקרוא למפה משרשורי העבודה שלה עצמה, כך שמימוש חייב להיות בטוח-שרשור. מונים משותפים, המטמון ותצפית ההגדרה כל אחד מוגן בתוך הרכיב על ידי מקטע קריטי משלו, אבל הקוד בתוך ResolveFont הוא שלכם לגרום לו להיות בטוח
הצורה הבטוחה ביותר היא ספק שלא נוגע בשום מצב משותף בר-שינוי: קריאה מטבלה שנבנתה בהפעלה, טעינת בייטים מקובץ או משאב, החזרה. אם חיפוש זקוק למטמון משותף משלכם, שמרו עליו. ושמרו חריגות בתוך המימוש שלכם, מכיוון שחריגת Pascal אסור לה לעולם להתפרק דרך מחסנית PDFium; הרכיב תופס בגבול ABI של C וממיר לכשל או לחזרה לברירת מחדל אופציונלית, אבל להסתמך על זה כזרימת בקרה רגילה עולה ביצועים ומסתיר באגים. כללי שרשור עבור שאר הרכיב עוקבים אחרי אותם עקרונות כמו אלה במשמעת נעילת הצגה
הוכחת המיפוי בייצור
סטטיסטיקות הופכות החלפת גופנים מניחוש למשהו שאפשר להצהיר עליו. GetSystemFontProviderStatistics מדווחת אם ספק מוגדר ומותקן, כמה בקשות מיפוי נעשו, ואיך הן סופקו, מחולק לפגיעות מטמון, פגיעות ספק ופגיעות חזרה-לברירת-מחדל, לצד תגובות שנדחו, בקשות שנכשלו, ידיות חיות וגופנים מומטמנים:
var
Stats: TPdfSystemFontStatistics;
begin
Stats := GetSystemFontProviderStatistics;
Writeln(Format('requests=%d cache=%d provider=%d fallback=%d',
[Stats.MapRequests, Stats.CacheHits, Stats.ProviderHits,
Stats.DefaultFallbackHits]));
Writeln(Format('rejected=%d failed=%d handles=%d cached=%d',
[Stats.RejectedProviderResponses, Stats.FailedRequests,
Stats.ActiveHandles, Stats.CachedFonts]));
// בריצת תאימות עם חזרה-לברירת-מחדל כבויה, כל פגיעת חזרה או
// בקשה שנכשלה אומרת שמסמך הפנה לגופן שאיננו משגרים
if (Stats.DefaultFallbackHits > 0) or (Stats.FailedRequests > 0) then
raise Exception.Create('unmapped font encountered - update the font set');
end;
ספירת RejectedProviderResponses העולה היא הסימן שספק עונה עם נתונים שהמדיניות דוחה, בדרך כלל קובץ גדול מדי או צורה מוחלפת, וכדאי להתריע על כך מכיוון שהבקשות האלה מתדרדרות בשקט לחזרה-לברירת-מחדל או לכשל. לאבחון אילו גופנים מסמך באמת צריך לפני בניית טבלת המיפוי, נתיב הבדיקה בניתוח תכונות גופן PDF רושם גופנים מוטמעים ולא-מוטמעים לכל מסמך
אספקת גופנים, הצגה וחילוץ טקסט חולקים את אותו מופע ספרייה בדלפי, C++Builder ו-Lazarus; פרטי פריסה מתוארים בעמוד רכיב PDFium Component לדלפי