חוברת עבודה של Excel יכולה לשאת תמונות EMF ו-WMF, והדרך המקובלת לצייר אחת היא למסור את stream הבייטים לנגן ה-metafile של מערכת ההפעלה. זו החלטה ששווה להביט בה ישירות: metafile הוא stream פקודות מסודר עבור API של גרפיקה, וניגון חוזר שלו פירושו לתת לקובץ שהגיע בדואר אלקטרוני להניע את מנהל ההתקן של הגרפיקה. HotXLS לוקח את הדרך השנייה. XLSDecodeVectorScene מנתח את ה-metafile בעצמו, מאמת את הכותרת, כל גודל רשומה, את מניין הרשומות המוצהר ואת המיקום המדויק של רשומת סוף-הקובץ, דוחה רשומות escape מכל וכל, ומחזיר TXLSVectorScene של פקודות ציור פרימיטיביות שה-backends של Canvas ו-SVG מנגנים דרך הקוד שלהם עצמם. שום ניגון מנהל התקן אינו מעורב בשום נקודה
העסקה היא כיסוי תמורת הכלה. רשימה לבנה של פקודות הממורכזת על מלבנים לא תשחזר כל metafile שמעצב יכול ליצור, ולכן הסצנה מדווחת כמה רשומות ציור היא לא יכלה לייצג והקורא מחליט מה לעשות עם זה. עבור תהליך שרת שמרנדר מסמכים שהוא לא יצר, העסקה הזאת היא בכיוון הנכון
למה ניגון metafile אינו מתאים לקלט לא מהימן?
כי הפורמט אינו תמונה, הוא תוכנית. stream רשומות EMF מפעיל מחסנית מצבים של הקשר מכשיר, מקצה ובוחר objects מטבלת מחוונים, ויכול לשאת רשומות escape שה-payload שלהן מועבר אל מנהל התקן. ניגון חוזר שלו מפעיל נתיבים בערמת הגרפיקה של הפלטפורמה שנכתבו מתוך הנחה שה-metafile בא מאפליקציה משתפת פעולה על אותו מחשב. כשהקלט הוא צרופת של גיליון נתונים, ההנחה הזאת נעלמה, ושום זהירות בתוך ספריית הגיליונות לא עוזרת כי הספרייה אינה הרכיב שמנתח
זהו אותו היגיון שמכתיב את שכבת המיכל. חוברת עבודה היא ארכיון ZIP, ו-HotXLS מאמת את ה-central directory שלה במקום לסמוך על היסת מוצהרות, כפי שמתואר במאמר אימות end-of-central-directory של ZIP. payload של metafile הוא השכבה הבאה של אותה בעיה
מה המפענח בודק לפני שהוא מצייר משהו
האימות מבני והוא מתרחש מראש, כי מנתח שמתחיל לצייר ומאמת תוך כדי כבר פעל על נתונים שלא אימת. הכותרת חייבת להתאים בקפדנות ולא בסבירות. כל רשומה חייבת להצהיר על גודל שנכנס בתוך החוצץ הנותר וגדול מספיק עבור השדות הקבועים שלה עצמה. מניין הרשומות שהכותרת מצהירה חייב להתאים לרשומות הקיימות בפועל. רשומת סוף-הקובץ חייבת לשבת בדיוק במקום שבו ה-stream מסתיים, ולא סתם איפשהו בקרבתו, מה שסותם את תרגיל הזבל העוקב שמסתיר payload שני מאחורי תמונה תקינה
מעבר למבנה, המפענח נכשל-סגור על הסמנטיקה. רשומות escape נדחות, לא מדולגות. רשומה שמשנה מצב שהמפענח אינו מדמה גורמת לפענוח להיכשל במקום להתעלם ממנה, כי התעלמות משינוי מצב פירושה שכל פקודת ציור עוקבת מבוצעת במצב שהקובץ לא ביקש, והתוצאה היא תמונה שגויה באופן שאף אחד לא יכול לחזות. רשומות ציור מחוץ לקבוצת הפקודות הנתמכת הן עניין אחר: הן נספרות ומדולגות, כי צורה חסרה היא פער נראה וניתן לדיווח ולא שחיתות שקטה
תקציבים הם חלק מהחוזה של הפורמט
לפורמטים וקטוריים יש גרסה משלהם לפצצת הדחיסה. כמה קילובייטים של רשומות יכולים להצהיר על polylines עם מאות מיליוני נקודות, או תמונה שהממדים המוצהרים שלה כופלים אל טרבייטים. הגבולות לכן חייבים להיות קבועים מפורשים ולא מה שהמחשב במקרה שורד
// מתוך lxVectorScene: תקציב הפענוח, מוצהר ולא מרומז
XL_VECTOR_MAX_RECORDS = 1000000;
XL_VECTOR_MAX_HANDLES = 4096;
XL_VECTOR_MAX_DC_DEPTH = 32;
XL_VECTOR_MAX_COMMANDS = 100000;
XL_VECTOR_MAX_POINTS_PER_RECORD = 100000;
XL_VECTOR_MAX_TOTAL_POINTS = 2000000;
XL_VECTOR_MAX_TEXT_CHARS = 4096;
XL_VECTOR_MAX_TOTAL_TEXT_CHARS = 1000000;
XL_VECTOR_MAX_IMAGE_SIDE = 8192;
XL_VECTOR_MAX_IMAGE_PIXELS = 32 * 1024 * 1024;
XL_VECTOR_MAX_IMAGE_BYTES = 64 * 1024 * 1024;
XL_VECTOR_MAX_COORD = 1000000000;
שניים מאלה ראויים להערה. תקרת העומק של הקשר המכשיר של 32 קיימת כי רשומות SaveDC ו-RestoreDC מקננות, ו-stream לא מאוזן יכול לדחוף לנצח; 32 נדיבה עבור metafiles אמיתיים וזולה לאכיפה. תקרת הקואורדינטות קיימת כי קואורדינטות מזינות טרנספורמציה, וערך ליד גבולות טווח המספרים השלמים מפיק תוצאה מומרת שהיא אינסופית או מתקפלת, ואחר כך כל חישוב bounding-box במורד הזרם חסר היגיון. הצמדת קואורדינטות בזמן ניתוח הרבה יותר קלה להבנה מאשר להגן על כל צרכן של הגאומטריה
שימוש בסצנה
המפענח מוסיר חזרה object שבבעלותכם, מניין פקודות, גודל נומינלי, ומניין רשומות ציור שבחר שלא לייצג
uses
lxVectorScene;
var
Scene: TXLSVectorScene;
Error: WideString;
I: Integer;
begin
// Data מחזיק את payload התמונה הגולמי שנלקח מהחוברת
if not XLSDecodeVectorScene(Data, xlsvfEmf, Scene, Error) then
begin
// נדחה: כותרת, גבולות, מניינים, מיקום EOF או תקציב
LogReject('metafile rejected: ' + Error);
Exit;
end;
try
if Scene.SkippedDrawRecords > 0 then
LogWarning(Format('%d drawing records outside the safe subset',
[Scene.SkippedDrawRecords]));
for I := 0 to Scene.Count - 1 do
case Scene.Commands[I].Kind of
xlsvcRectangle: DrawRect(Scene.Commands[I]);
xlsvcEllipse: DrawEllipse(Scene.Commands[I]);
xlsvcPolyline,
xlsvcPolygon,
xlsvcBezier: DrawPath(Scene.Commands[I]);
xlsvcText: DrawText(Scene.Commands[I]);
xlsvcImage: DrawImage(Scene.Commands[I]);
end;
finally
Scene.Free;
end;
end;
רשומת הפקודה נושאת הכול ש-backend צריך ושום דבר שדורש התקן: נוכחות עט, צבע, עובי וסגנון; נוכחות מברשת וצבע; הגאומטריה; ולטקסט המחרוזת, שם הגופן, הגודל, הסגנונות והיישור. זה מה שהופך את אותה סצנה שמישה גם על ידי מרנדר ה-canvas על המסך וגם על ידי הכותב של SVG, וזו הסיבה שהנתיב הווקטורי לא מסתעף בין תצוגה מקדימה לייצוא. רינדור מסך של תוכן גיליון באופן כללי מכוסה במאמר רינדור ה-grid המותאם של VCL
דחיית תמונה אינה מזיקה לחוברת
תכונה חשובה של העיצוב הזה היא שפענוח נדחה משפיע על הרינדור בלבד. ה-payload המקורי נשאר במודל, ולכן חוברת עבודה שנפתחת ונשמרת שוב מוציאה את תמונות ה-metafile שלה בייט-בייט, בין אם המפענח הבטוח יכול לצייר אותן ובין אם לאו. נתיב הרסטר המסוגר הקיים גם נשאר זמין כ-fallback. מילים אחרות, המנתח הקפדני שוער את מה שמבוצע, לא את מה שנשמר, וזו ההבחנה שמאפשרת לשינוי מונע-אבטחה לצאת בלי להפוך לשינוי אובדן נתונים
טיפול ב-objects של ציור באופן כללי, כולל החלקים של מודל ה-objects ששורדים round-trip ללא מגע, מכוסה במאמר תרשימים, תמונות וציורים
איפה זה משאיר פריסת שרת
אם אתם מרנדרים חוברות עבודה שהועלו על ידי משתמשים בשירות, העמדה המעשית היא עכשיו בת הגנה: תמונות metafile מנותחות על ידי קוד שאתם יכולים לבקר, מסוגרות על ידי קבועים שאתם יכולים לקרוא, ולעולם לא מוסרות למנהל התקן גרפיקה. ההסתייגות הכנה היא הכיסוי. metafiles מורכבים שהופקו על ידי כלי ציור יפגעו במונה הרשומות המדולגות, והתשובה לזה היא לחשוף את המונה ולא להרחיב בשקט את הרשימה הלבנה. תמונה שמוצגת חלקית ואומרת זאת היא שיחת תמיכה; תמונה שמוצגת שגוי ולא אומרת דבר היא דיווח באג מלקוח
HotXLS מטפל ב-XLS, XLSX, ODS ו-CSV באופן מקורי ב-Delphi ו-C++Builder בלי Excel מותקן, ואותה פילוסופיה של ניתוח מסוגר רצה דרך שכבות המיכל, הנוסחאות והציור שלו. פרטי פורמט ואבטחה מופיעים בדף המוצר של HotXLS Delphi spreadsheet component