PDFlibPas ממיר enhanced metafiles לתוכן עמוד PDF אמיתי רשומה אחר רשומה, במקום להמיר אותן לרסטר, וזה מה שמשאיר תרשים מיובא או שרטוט CAD חד בכל רמת זום. הממיר הזה הוא בן כ-6500 שורות ונכתב מול ה-VCL, ולכן כשהספרייה קיבלה יעד Free Pascal הוא סווג כבלתי נייד והוחלף ב-stub. הסיווג הזה היה שגוי, ואופן הטעות הוא לקח שימושי לגבי איך לבקר תלות לפני שמחליטים לכתוב סביבה מחדש
ממשק ה-VCL האמיתי של אותן 6500 שורות התברר כקטן: מחלקת bitmap שנעשה בה שימוש עבור פורמט הפיקסלים, שמירת stream, מחוון, canvas ו-scanlines; מחלקת metafile שנעשה בה שימוש עבור הרוחב, הגובה והמחוון שלה; וטיפוס הצבע עם שני קבועים. כל אחד מאלה כבר סופק על ידי יחידת הגרפיקה של הספרייה עצמה, שקיימת בדיוק כדי שלבנייה ללא VCL יהיו מקבילות. הממיר לא היה חסום על ה-VCL בכלל. הוא היה חסום על יחידת ה-Windows של Free Pascal
פיצול על הציר שהקוד באמת תלוי בו
לכן השינוי לא היה מימוש מחדש. הוא היה conditional אחד: מ"קמפלים את ה-stub כשבונים בלי VCL" ל"קמפלים את ה-stub כשלא בונים ל-Windows". זה הציר הנכון, וניסוח הסיבה עושה את ההבדל למובן. enhanced metafile הוא מיכל של Windows. הממיר הוא מנתח של רשומות GDI של Windows מקצה לקצה. לשימוש של האפליקציה המארחת ב-VCL, בערכת widgets אחרת או באף ערכה כלל אין שום קשר לשאלה אם אפשר לפרש את הרשומות האלה; לשאלה אם היעד הוא Windows יש קשר לכל דבר
ההשלכות של בחירת הציר הנכון מגיעות במתנה. בנייני C++Builder, שמבטלים את הגדרת סמל הפלטפורמה של Windows בספרייה הזאת, שומרים על ה-stub הזורק חריגות ומתנהגים בדיוק כקודם. macOS שומר על ה-stub, בצדק, כי אין שם רשומות GDI לניתוח. בנייני Delphi VCL אינם נפגעים. ובניית Windows עם ערכת widgets שאינה VCL מקבלה יבוא EMF וקטורי כתופעת לוואי, שאף אחד לא היה צריך לממש. conditional שמיושר לתלות האמיתית הופך עבודת פלטפורמה לשינוי של שורה אחת; conditional שמיושר לציר השגוי הופך אותה לכתיבה מחדש שלא מגיעה ללוח הזמנים אף פעם
הפער של Free Pascal היה הצהרות, לא לוגיקה
מה שחסר באמת היו ההצהרות של Win32 שיחידת ה-Windows של Delphi מספקת ושזו של Free Pascal אינה מספקת. איסופן ליחידת תאימות אחת במקום פיזור תנאים בכל הממיר שמר על המנתח קריא. הרשימה למדנית כי היא מראה עד כמה כיסוי ה-headers בלתי אחיד בין שני ה-RTL: 113 קבועי טיפוס רשומות metafile, שני דגלים של פלט טקסט מורחב, שלושה קבועי מצב מילוי gradient, טיפוס מחוון לטבלת מחוונים, כינויים לרשומות ה-vertex והפרימיטיבים של gradient, ושלושה טיפוסי רשומות ש-Free Pascal לא מצהיר עליהם בכלל, המכסים alpha blending, blitting שקוף ומצב ניהול צבע
אף פריט מזה אינו מעניין לבדו. הכול חייב להיות נכון לפני שהמנתח מתקמפל, ויחידת תאימות היא הבית הטבעי כי אפשר להשוות אותה ב-diff מול תיעוד ה-headers כיחידה אחת
זו שמציירת בשקט את התמונה השגויה
שתיים מההצהרות האלה אינן רק חסרות, הן קיימות ושגויות למטרה הזאת, וזה החלק ששווה לזכור גם אם לעולם לא תיגעו ב-metafile
Free Pascal מצהיר על רשומת יצירת המברשת עם מבנה המברשת של זמן הריצה המוטמע בה, ועל רשומת העט המורחב עם מבנה העט של זמן הריצה המוטמע בה. שני המבנים של זמן הריצה מצהירים על החבר hatch שלהם כמספר שלם בגודל מחוון, כי בקריאת GDI חיה אותו חבר יכול לשאת handle. metafile, לעומת זאת, תמיד מאחסן את הצורה בת 32 הביט, כי הפריסה של הרשומה היא חלק מפורמט הקובץ המסודר ואינה משתנה עם רוחב הביטים של התהליך
בבניינים בני 32 ביט שניהם מסכימים ושום דבר לא קורה. ב-Win64 החבר בגודל מחוון הוא שמונה בייטים במקום שבהם לקובץ יש ארבעה, ולכן כל שדה אחרי החבר hatch נקרא מהיסה שגויה. אין חריגה, אין שגיאת ניתוח ואין אזהרה. ה-metafile פשוט מוצג שגוי: צבעים מהבייטים השגויים, עובי עט מהבייטים השגויים, ותמונה שנראית כמו באג תצוגה ולא כמו באג פריסת struct. Delphi משלחת גרסאות בנות 32 ביט מפורשות של שני המבנים בדיוק מהסיבה הזאת, ויחידת התאימות מצהירה עליהן מחדש באותה צורה
// שגוי ב-Win64: Hatch בגודל מחוון, הקובץ מאחסן 32 ביט,
// וכל שדה עוקב מוסט בארבעה בייטים ללא שגיאה
type
TLogBrushRuntime = record
lbStyle: UINT;
lbColor: COLORREF;
lbHatch: ULONG_PTR; // 8 בייטים בתהליך בן 64 ביט
end;
// נכון: הפריסה המסודרת, רוחב קבוע ללא קשר לרוחב הביטים
type
TLogBrush32 = record
lbStyle: UINT;
lbColor: COLORREF;
lbHatch: DWORD; // תמיד 4 בייטים, כפי שנאחסן ב-metafile
end;
הכלל הכללי: כל מבנה שמופיע גם כארגומנט API של זמן ריצה וגם כפריסת שדות מסודרת זקוק לשתי הצהרות, והמסודרת חייבת להשתמש בטיפוסים ברוחב קבוע מקצה לקצה. חברים בגודל מחוון בפורמט קובץ הם תמיד באג שמחכה לבנייה בת 64 ביט
הבדלי חתימות שייכים ל-wrapper, לא בכל אתר קריאה
ההבדלים הנותרים היו חוסרי התאמה רגילים של חתימות, והדרך לספוג אותם היא wrapper מעביר ולא conditional בכל אתר קריאה. הפונקציה לשילוב מטריצות מקבלת מחוונים תחת Free Pascal במקום שבו Delphi מקבלת פרמטרי התייחסות, ולכן ה-wrapper מקבל התייחסויות ומעביר כתובות. הוא גם מעתיק קודם את שני ארגומנטי המקור למשתנים מקומיים, כי לממיר יש אתרי קריאה שבהם מטריצת היעד היא בו בזמן אחד המקורות, והעברת אותה כתובת פעמיים לפונקציה שכותבת תוך כדי קריאה מפיקה מטריצה שגויה בעדינות באופן שמופיע רק על תוכן מסובב
function CombineTransformCompat(var Dest: TXForm;
const A, B: TXForm): BOOL;
var
SrcA, SrcB: TXForm;
begin
// קודם העתקה: קוראים מעבירים באופן לגיטימי את Dest כ-A או כ-B
SrcA := A;
SrcB := B;
{$IFDEF FPC}
Result := Windows.CombineTransform(@Dest, @SrcA, @SrcB);
{$ELSE}
Result := Windows.CombineTransform(Dest, SrcA, SrcB);
{$ENDIF}
end;
טיפוסי ה-rectangle וה-point הם המקרה האחר. Free Pascal מתייחס לרשומות ה-rectangle וה-point של ה-metafile כטיפוסים נפרדים מהכלליים של הגרפיקה, ולכן שמונה אתרי הצבה היו זקוקים להמרה מפורשת בין רשומות בעלות פריסה זהה. שני המהדרים מקבלים את צורת ההמרה, ולכן האתרים האלה אינם נושאים conditional בכלל, מה ששווה מעט מכוער
מה זה משנה לפריסת Free Pascal
יבוא EMF וקטורי עובד ב-Windows תחת Free Pascal ומפיק את אותו תוכן עמוד כמו הבנייה של Delphi: נתיבים כנתיבים, gradients כתוכן pattern, טקסט כטקסט. מחוץ ל-Windows הנתיב הרסטרי נשאר התשובה, וזו מגבלה של הפורמט ולא של ההעברה. מצב הקואורדינטות וה-clipping שאליו הממיר מזין מתואר במאמר מעקב ה-CTM וה-clipping של content stream, והפרימיטיבים הווקטוריים שהוא פולט מכוסים בגרפיקה וקטורית, shaders ו-gradients
אם אתם מבקרים את בסיס הקוד שלכם לגבי הזדמנות דומה, התרגיל השימושי הוא זה שהתחיל את הסיפור: רשמו את החברים שאתם באמת משתמשים בהם מהפריימוורק שלדעתכם אתם תלויים בו. התשובה קצרה לרוב הרבה יותר ממה שרשימת ה-import מרמזת, והאילוץ האמיתי נמצא בדרך כלל במקום אחר לגמרי. נתיבי יבוא מבוססי device context מתוארים באופן כללי במאמר תצוגה מקדימה להדפסה ו-device context, וכיסוי פלטפורמות ו-toolchains מופיע בדף המוצר של losLab PDF Developer Library