PDFlibPas יכול לסמן מסמך בזמן שהוא מצויר. הפעל את SetAutoTagMode וקריאות DrawText רגילות הופכות לפסקאות, טקסט שמצויר מיד אחרי RegisterHeading הופך לכותרת ברמה זו, כותרות עליונות ותחתונות רצות הופכות לחפצי עזר שהקורא מדלג עליהם, תמונות הופכות לאיורים, ו-DrawTableRows נושא את הטבלה, השורות והתאים שלה אל עץ המבנה
החלופה — ועד לאחרונה האפשרות היחידה — הייתה לעטוף כל קריאת ציור ב-BeginTag ו-EndTag באופן ידני. זה עובד, ולמסמכים בעלי מבנה חריג זה עדיין הכלי הנכון. עבור דוח, חשבונית או דף-הבנק רגילים, פירוש הדבר שהנגישות של הפלט תלויה בכך שאף אחד לא שכח זוג, לאורך כל נתיב הקוד שמצייר משהו
על מה מכסים סיביות המצב
SetAutoTagMode מקבל מסכת סיביות ומחזיר את המצב שהיה בתוקף קודם. AUTOTAG_TEXT (1) מסמן טקסט כפסקה, או ככותרת כשאחת מתחייבת. AUTOTAG_FURNITURE (2) מסמן כותרות עליונות, תחתונות ומספרי עמוד כחפצי עזר. AUTOTAG_FIGURE (4) הופך תמונה מצוירת לאיור, או לחפץ עזר כשהוגדרה כדקורטיבית. AUTOTAG_TABLE (8) נושא טבלאות מצוירות אל עץ המבנה. AUTOTAG_DEFAULT הוא 15, כלומר כל הארבעה
הפעלת המצב גם מסמנת את המסמך כמתויג, והצעד הזה פחות קוסמטי ממה שהוא נשמע. קורא מתייחס למסמך כלא-מתויג אלא אם הקטלוג אומר אחרת (ISO 32000-1 §14.7.1), כך שקובץ שנושא עץ מבנה מלא ללא הצהרת /MarkInfo מוכרז על ידי טכנולוגיה מסייעת כחסר מבנה כלל. העץ קיים; שום דבר לא קורא אותו
var
Lib: TPDFlib;
begin
Lib := TPDFlib.Create;
try
Lib.SetOrigin(1);
Lib.SetAutoTagMode(AUTOTAG_DEFAULT); // text + furniture + figures + tables
Lib.AddStandardFont(4);
Lib.SetTextSize(18);
Lib.RegisterHeading(1, 'Annual service report');
Lib.DrawText(72, 96, 'Annual service report'); // becomes H1
Lib.SetTextSize(11);
Lib.DrawText(72, 130, 'Every unit installed before 2024 was inspected.');
Lib.SaveToFile('report.pdf');
finally
Lib.Free;
end;
end;
איך כותרת יודעת לאיזה טקסט היא שייכת?
RegisterHeading מציין את הרמה לטקסט הבא שייצויר, והוא ממתין לטקסט. אם תמונה מצוירת ביניהם, התמונה הופכת לאיור והכותרת נשארת ממתינה לטקסט שאחריה. התנהגות זו מכוונת: החלופה, שבה התמונה לוקחת את רמת הכותרת, ייצרה מסמכים שבהם קו מפריד דקורטיבי מתחת לכותרת הוכרז ככותרת עצמה
אותו כלל "מתבזר על פריט אחד" חל על איורים. RegisterFigure מספק את התיאור שהתמונה הבאה תישא, ו-RegisterDecoration מצהיר שהתמונה הבאה היא קו מפריד, מסגרת או רקע חסר משמעות. שניהם נצרכים על ידי תמונה אחת, כך שתמונה מאוחרת יותר לעולם לא יורשת תיאור שיועד לתמונה קודמת — וזה בדיוק איך טקסט חלופי מגיע להיות מחובר לתמונה הלא-נכונה בקוד המתויג ידנית
התיאור חשוב יותר מכל מחרוזת בודדת אחרת במסמך נגיש. קורא עיוור מקבל את התיאור במקום התמונה, וזה כל מה שהוא מקבל. "תרשים" אינו תיאור; "הכנסה רבעונית לפי אזור, כשהאזור המזרחי גבוה ביותר ברבעון השלישי" — זה תיאור
Lib.RegisterFigure('Exploded view of the gearbox assembly');
Lib.AddImageFromFile('gearbox.png', 0); // becomes a tagged Figure
Lib.RegisterDecoration; // meaningless rule
Lib.AddImageFromFile('divider.png', 0); // drawn inside a layout artifact
טבלאות, כותרות והיכן נמצאת החלטת החזרה
כשסיבית הטבלה פעילה, DrawTableRows נושא את הטבלה, השורות והתאים שלה אל עץ המבנה, כך שקורא יכול לומר באיזו עמודה נמצא ערך במקום לקרוא את כל הטבלה כרצף טקסט לא קשור. SetTableHeaderRowCount מציין כמה שורות מובילות הן כותרות; שורות אלה נכתבות כתאי כותרת שנושאים היקף עמודה, וזה מה שמאפשר לקורא להכריז על הכותרת של הערך שעליו נמצא המשתמש
שורות כותרת שמצוינות כך נשארות במקומן. החזרתן בראש כל עמוד היא החלטת פריסה, והיא נשארת כזו: DrawTaggedTableRows מקבל ארגומנט RepeatHeaderRows בדיוק למטרה זו. השארת השניים נפרדות מונעת מעץ המבנה לרכוש עותק שני של הכותרת לכל מעבר עמוד, שזה מה שחזרה אוטומטית הייתה מייצרת
var
TableID: Integer;
begin
TableID := Lib.CreateTable(40, 3);
Lib.SetTableHeaderRowCount(TableID, 1); // row 1 is the header band
Lib.SetTableCellContent(TableID, 1, 1, 'Part');
Lib.SetTableCellContent(TableID, 1, 2, 'Torque');
Lib.SetTableCellContent(TableID, 1, 3, 'Unit');
// ... fill the data rows ...
// Draw rows 1..40 into a 600pt band, repeating one header row per page
Lib.DrawTaggedTableRows(TableID, 72, 150, 600, 1, 40, 1);
end;
שילוב של סימון אוטומטי וידני
סימון אוטומטי נעמד בצד בתוך תג שנפתח ידנית. חלק ממסמך יכול להיות מתואר על ידי הקוד שלך ואילו השאר מושאר לספרייה, מבלי שהשניים יקננו זה בזה — וזה הסידור שרוב המסמכים האמיתיים רוצים. לעמוד השער ולבלוק החתימה יש מבנה שרק אתה מבין; מאתיים העמודים של גוף הטקסט שביניהם לא
שני כללי בטיחות שומרים על הפלט נקי. שום דבר לא מתויג בתוך חפץ עזר, משום שתוכן המסומן כחפץ עזר אסור שיישא אלמנט מבנה. וטקסט ריק לא פותח אלמנט, כך ש-DrawText תועה עם מחרוזת ריקה לא יכול לייצר אלמנט מבנה שקורא היה מכריז עליו כריק. שניהם מסוג הליקויים שמסמכים מתויגים ידנית צוברים בשקט ושמאמת מדווח בכמות חודשים אחר כך
מה סימון אוטומטי עדיין לא מכריע עבורך
סדר קריאה שמעבר לסדר הציור, תפקידים סמנטיים שאינם פסקה, כותרת, איור או טבלה, והצהרות שפה. סימון אוטומטי מקצה מבנה בסדר שבו התוכן מצויר — אם קוד הפריסה שלך מצייר את סרגל הצד לפני הגוף, זה הסדר שהעץ רושם. עבור מסמכים שבהם הסדר החזותי וסדר הקריאה שונים באופן מהותי, ממשק ה-API הידני לסימון נשאר הכלי הנכון, והסקירה של PDF מתויג ומבנה נגישות מכסה תפקידים, היקפים וכריכות כותרת בפירוט
כשהמסמך מוכן, יש לאמת ולא להניח: ההערות על preflight של PDF/A ו-PDF/UA מראות איך לקבל פסק דין על המבנה שייצרת, והסקירה של ייצוא דוחות מונחה-נתונים מכסה את מקומן של קריאות אלה במנוע דוחות שמייצר את הפריסה שלו מתוך הנתונים
PDFlibPas היא ספריית PDF מקורית בפסקל לדלפי, C++Builder ו-Lazarus ללא סביבת ריצה חיצונית של PDF, כך שפלט נגיש מיוצר על ידי אותו קוד שמצייר את המסמך — ראה את דף המוצר של PDFlibPas לרשימת ה-API והפלטפורמות המלאה