הצללה שמתרנדרת כבלוק אפור שטוח אחד היא הכשל הקלאסי של דוגמת ריצוף. HotPDF, רכיב ה-VCL PDF הילידי עבור דלפי ו-C++Builder, מציירת PatternType 1 על ידי הפיכת הנתיב הנוכחי לחיתוך זמני ושחזור זרם תוכן הדוגמה פעם אחת לכל אריח גלוי, כאשר בחירת הדוגמה מוחזקת במצב הגרפיקה ומשוחזרת על ידי q ו-Q
התסמינים מגיעים בשני טעמים, והם נראים לא-קשורים עד שאתה יודע את הסיבה. שרטוט CAD מאבד את הצללת החתך שלו וחוזר כמילויים מוצקים, כי הרינדור פתר את הדוגמה לצבע ממוצע וצייר אותו. או שההצללה בורחת: בלוק כותרת שאמור להיות לבן פשוט קולט את הקווים האלכסוניים מתצוגת פירוט שני נתיבים קודם. שני אלה הם בעיות מצב-דוגמה, ורק אחת מהן קשורה בכלל לציור אריחים
למה דוגמת ריצוף דולפת לנתיב הבא?
מפני ששם הדוגמה הנבחר הוא חלק ממצב הגרפיקה, לא תכונה של האופרטור שהשתמש בו. תקן ISO 32000-1 §8.6.6.2 מגדיר מרחב צבע Pattern כאחד שערך הצבע שלו הוא שם דוגמה שסופק ל-scn או SCN, וכל רכיב אחר של מצב הצבע נשמר על ידי q ומשוחזר על ידי Q. שם הדוגמה חייב לעקוב אחרי אותו כלל. HotPDF שומרת אותו ברשומת המצב כ-FillPatternName ו-StrokePatternName, לצד משפחת מרחב הצבע של המילוי והקו, כך ש-Q מחזיר את הבחירה הקודמת בדיוק כמו שהוא מחזיר את ה-CTM הקודם
שמור את השם הזה במשתנה מקומי בתוך שולח האופרטורים במקום זאת, והוא שורד כל Q בזרם. הכישלון אז מופיע במקום בלתי-צפוי: Form XObject שנצייר אחרי הנתיב המדוגם יורש בחירת דוגמה שזרם התוכן שלו עצמו מעולם לא ביצע, והמילויים שלו יוצאים מוצללים. טפסים מקוננים מחמירים את זה, כי כל רמת קינון דוחפת ומשחזרת מצב שהמשתנה התועה מתעלם ממנו. קביעת מרחב צבע לא-דוגמה עם cs או CS, או הוצאת g / rg / k פשוט, חייבת גם לנקות את שם הדוגמה, אחרת הבחירה המיושנת שורדת מעבר למרחב הצבע שנתן לה משמעות
q
/Pattern cs % pattern colour space, ISO 32000-1 8.6.6.2
/P1 scn % coloured tiling pattern, PaintType 1
10 10 200 120 re f % this rectangle is hatched
Q
0 0 300 200 re f % must be black again, not hatched
q
/Cs2 cs % [/Pattern /DeviceCMYK] array
0 0.6 1 0 /P2 scn % uncoloured pattern plus its underlying colour
20 20 160 90 re f*
Q
דוגמה מצוירת דרך חיתוך, לעולם לא כמילוי
המודל הנכון הוא חיסורי: הגבל את חיתוך ההתקן לצורה המצוירת, ואז הרץ את תוכן הדוגמה בתוכה. HotPDF אף פעם לא מציירת קירוב מוצק תחילה ומציירת מעליו, כי המוצק הביניים היה נראה דרך הפערים בין אריחים ומתנגש עם כל שקיפות בתוכן האריח. §8.7.3.2 מתאר דוגמת ריצוף כזרם תוכן שמשוכפל במרווחים אופקיים ואנכיים קבועים, ושכפול הגיוני רק כנגד חיתוך שכבר יש לו את הצורה הנכונה. עבור מילויים ההמרה ישירה: HPDFSelectFillPathClip קובעת את מצב מילוי הפוליגון ל-ALTERNATE עבור f*, B* ו-b* ול-WINDING עבור הגרסאות nonzero, בונה את נתיב ה-GDI, וחותכת אותו לתוך החיתוך עם SelectClipPath. השורה היחידה הזו היא מה שגורם למילוי מדוגם אי-זוגי-זוגי להשאיר את אותם חורים כמו מילוי מוצק אי-זוגי-זוגי, וזה בדיוק מה שאזור מוצלל בצורת סופגנייה צריך
קווים הם החלק שקל לטעות בו. לנתיב שיש לו קו אין פנים, כך שחיתוך הנתיב עצמו לתוך החיתוך מניב אזור ריק ושום דבר לא מצויר. HPDFSelectStrokePathClip לכן בונה עט גיאומטרי ממצב הגרפיקה הנוכחי תחילה, באמצעות PS_GEOMETRIC עם המכסה מ-J, הצירוף מ-j, מגבלת המייטר מ-M, ו-PS_USERSTYLE כשמערך מקפים פעיל, ואז קוראת ל-WidenPath כדי להמיר את המתאר המקווקו לאזור ניתן-למילוי לפני החיתוך. התנהגות המכסה, הצירוף, המייטר והמקף בנתיב מדוגם-בקו אז תואמת קו רגיל בבנייה ולא במימוש שני. שני מגבלות כנות חיות כאן: רוחבי קו מתחת ליחידת התקן אחת מהודקים לפיקסל אחד, ומערך המקפים נחתך בשש עשרה רשומות, שהיא התקרה ש-ExtCreatePen מקבלת
אילו אריחים באמת גלויים?
הטווח הגלוי מגיע מהרצת הטרנספורם לאחור. מיקום אריחים קורה במרחב הדוגמה, אבל הדבר היחיד שיודע כמה מהעמוד נוגעים בו הוא תיבת החיתוך של ההתקן, שנמצאת במרחב ההתקן. HotPDF מרכיבה BaseMatrix := CTM * PatternMatrix, הופכת אותה, וממפה את ארבעת הפינות של תיבת חיתוך ה-GDI חזרה דרך ההפכית. הגבולות המיושרים-לציר של ארבעת הפינות הממופות נותנים את מלבן מרחב-הדוגמה שיכול אפשרית להיות מכוסה, וחלוקת המלבן הזה לפי XStep ו-YStep כנגד BBox הדוגמה נותנת טווחי אינדקס סגורים. כל תא אז מתרנדר עם CTM של CTM * PatternMatrix * Translate(i * XStep, j * YStep), ונחתך בפעם שנייה למצולע BBox המתורגם שלו עצמו. החיתוך השני הזה חשוב כש-XStep קטן מרוחב תיבת הגבולות, שזה איך עיצובי אריח חופפים באים לידי ביטוי; בלעדיו, תאים שכנים היו מציירים אחד מעל השני מחוץ להיקף המוצהר שלהם. אם החיתוך לכל-תא חוזר כ-NULLREGION, התא מדולג ללא טוקניזציה או הרצה של דבר
// Map the device clip box back into pattern space through the inverse of
// CTM * PatternMatrix, then convert those bounds into tile index ranges.
BaseMatrix := HPDFMatMul(FGSStack.State.CTM, PatternMatrix);
if not HPDFMatInvert(BaseMatrix, InverseMatrix) then Exit; // singular: refuse
if GetClipBox(FDC, ClipRect) = ERROR then Exit;
// MinX..MaxY are the axis-aligned bounds of the four mapped clip corners.
I0 := Floor((MinX - BBox[2]) / StepXAbs);
I1 := Ceil ((MaxX - BBox[0]) / StepXAbs);
J0 := Floor((MinY - BBox[3]) / StepYAbs);
J1 := Ceil ((MaxY - BBox[1]) / StepYAbs);
PlannedTiles := Int64(I1 - I0 + 1) * Int64(J1 - J0 + 1);
if (PlannedTiles <= 0) or (PlannedTiles > FPatternTilesRemaining) then Exit;
Dec(FPatternTilesRemaining, Integer(PlannedTiles));
דוגמאות לא-צבועות והצבע שמגיע מבחוץ
דוגמת PaintType 2 נושאת צורה אך לא צבע, והצבע מגיע עם שם הדוגמה. §8.7.3.2 מציין שדוגמה לא-צבועה משמשת רק עם מרחב צבע Pattern שמצהיר על מרחב תשתית, כך ש-scn מקבלת את ערכי הרכיב תחילה ואת שם הדוגמה אחרון. HotPDF פותרת את הרכיבים האלה דרך מרחב התשתית שמאוחסן ברשומת מרחב צבע הדוגמה, מה שאומר שהצללה לא-צבועה יכולה להיצבע עם דיו Separation או שילוב DeviceN בדיוק כמו כל מילוי אחר; המכניקה של פתרון זה מכוסה ברינדור צבעי Separation ו-DeviceN spot. בתוך האריח, שני סוגי הצביעה מתפצלים בחדות. עבור PaintType 2 הרינדור קובע דגל דיכוי-אופרטור-צבע למשך האריח, כך שכל g, rg, k או scn בתוכן הדוגמה מתעלם וכל סימן לוקח את הצבע שסופק חיצונית. עבור PaintType 1 ההיפך חל: מצב מילוי וקו מאופסים לברירות המחדל של PDF, DeviceGray שחור עם מרחב צבע זהות, והאריח צובע את עצמו. דילוג על האיפוס הזה מאפשר לצבע שהיה במקרה נוכחי באופרטור f לדלוף לתוך דוגמה שהייתה אמורה להיות מתארת-את-עצמה
למה עומק ערימת מצב הגרפיקה חייב להתאושש אחרי כל אריח?
מפני שזרם תוכן דוגמה מותר להיות לא-מאוזן, והנזק מצטבר על פני תאים. אריח שהזרם שלו מכיל שלושה אופרטורי q ושני אופרטורי Q משאיר את הערימה עמוקה פריים אחד יותר ממה שהתחילה. שחזר רק את רשומת המצב הנוכחית בין תאים והעומק ממשיך לגדול, כך שתא מספר מאתיים מבוצע ממסגרת ערימה ששייכת לתא מספר מאה תשעים ותשע, עם איזה CTM וחיתוך שהמסגרת ההיא נשאה. HotPDF לכן מצלמת את רשומת המצב ואת עומק הערימה לפני לולאת האריחים וקוראת ל-RestoreSnapshot בראש כל איטרציה, מה שקוצץ את הערימה בחזרה לאורך השמור ומתקין מחדש את המצב השמור בצעד אחד. מילון המשאבים של העמוד ודגל דיכוי-אופרטור-הצבע משוחזרים באותו גבול, כיוון שאריח עשוי להפנות למשאבים שלו עצמו ואסור לו למסור אותם לשכנו. מצב חיתוך GDI מקבל את אותו טיפול דרך זוג SaveDC / RestoreDC סביב כל תא, כך שאריח שמתקין חיתוך W n משלו לא יכול לכווץ את האזור הזמין לבא אחריו
תקציבים, סירובים, ומה הרינדור לא יצייר
דוגמאות ריצוף הן המקום הקל ביותר ב-PDF לכתוב קובץ מניעת-שירות, כך שהמגבלות הן מספרים קשיחים ולא היוריסטיקות. קינון דוגמאות מוגבל לעומק 4, אותה הגנה שמשמשת עבור רקורסיית Form XObject, מה שעוצר דוגמה שמפנה לעצמה דרך מילון המשאבים שלה עצמה. ציור נתיב בודד יכול להריץ לכל היותר 16,384 אריחים בסך הכול, נספרים כלפי מטה על פני דוגמאות מקוננות ומתאפסים רק כשציור הדוגמה החיצוני-ביותר מתחיל. רשת אריחים שמספר התאים המתוכנן שלה חורג ממה שנשאר מהתקציב הזה נדחית לחלוטין, לפני שתא בודד רץ
גיאומטריה מנוונת נדחית ולא מקורבת. BBox חסר או בעל-שטח-אפס, XStep או YStep שהגודל שלו מתחת ל-1e-6, מכפלת CTM * PatternMatrix ללא הפכית, קואורדינטות חיתוך ממופות מעבר ל-1e9, או גודל אינדקס מעבר למיליון כולם גורמים לציור הדוגמה לחזור בלי לצייר. התוצאה היא אזור לא-מצויר במקום thread רינדור תקוע, וזה הטרייד-אוף שאתה רוצה בממיר אצוות. הביצועים מגיעים מהחלטה אחת: זרם הדוגמה עובר טוקניזציה פעם אחת לכל ציור עם HPDFTokenizeContentStream ומערך הטוקנים משמש שוב על פני כל תא גלוי, כך שספירת אריחים מכפילה את עלות הביצוע אך לעולם לא את עלות ה-lexing
רינדור עמוד מדוגם מדלפי
שום דבר בתמיכת הדוגמאות לא משנה את קוד הקריאה. טען את המסמך, בקש עמוד, ועבודת הריצוף קורית בתוך מפרש זרם-התוכן שרינדור עמוד-לביטמאפ כבר מפעיל. אותו מפרש מזין הקשרי התקן ביטמאפ, metafile ומדפסת, כך ששרטוט מוצלל שנראה נכון בתמונה ממוזערת של תצוגה מקדימה מודפס עם אותה גיאומטריית אריח. דוגמאות הצללה PatternType 2 לוקחות ענף שונה ששותף בנתיב ההערכה שלו עם אופרטור sh החשוף, מתואר בפירוט תחת רינדור הצללות צירי ורדיאלי
var
Pdf: THotPDF;
Bmp: TBitmap;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('assembly-drawing.pdf') > 0 then
begin
// Section hatching that previously flattened to a solid block now
// replays the tile content once per visible cell.
Bmp := Pdf.RenderLoadedPageToBitmap(0, 200);
if Assigned(Bmp) then
try
Bmp.SaveToFile('sheet1.bmp');
finally
Bmp.Free;
end;
end;
finally
Pdf.Free;
end;
end;
כשאזור מדוגם עדיין נראה שגוי, בדוק את שלוש מחלקות הכישלון בסדר. אזור שריק לחלוטין בדרך כלל אומר סירוב: בדוק את XStep, YStep ו-BBox לערכים מנוונים, או ספור את האריחים שהרשת הייתה צריכה כנגד תקרת 16,384. אזור שצויר בצבע שטוח יחיד אומר ששם הדוגמה מעולם לא הגיע לאופרטור הציור, מה שמצביע על סדר cs ו-scn בזרם. דוגמה שמופיעה איפה שהיא לא שייכת אומרת שחזור מצב, והמקום להסתכל בו הוא הטיפול ב-q / Q סביב הטופס או הנתיב שירש אותה
דוגמאות ריצוף הן אחת מהתכונות האלה של PDF שנשארות בלתי-נראות עד שהקובץ שזקוק להן נוחת בתיבת הדואר הנכנס שלך, ואז הן כל העבודה. אם אתה בונה צופי שרטוט, ממירי מסמכים הנדסיים או מרנדרי דוחות בדלפי או C++Builder, הרכיב המלא וה-API הרינדור שלו מתועדים בעמוד HotPDF Delphi PDF component