مقاله فنی

رندر الگوی Tiling در PDF با Delphi و HotPDF

هاشور که به‌عنوان یک بلوک خاکستری تخت واحد رندر می‌شود شکست کلاسیک الگوی tiling است. HotPDF، کامپوننت VCL بومی PDF برای Delphi و C++Builder، PatternType 1 را با تبدیل مسیر فعلی به یک clip موقت و پخش مجدد content stream الگو یک‌بار به‌ازای هر tile قابل‌مشاهده رنگ می‌کند، با انتخاب الگو نگه‌داشته‌شده در وضعیت گرافیکی و بازیابی‌شده توسط q و Q

علائم در دو طعم می‌آیند، و تا زمانی که علت را ندانید بی‌ربط به‌نظر می‌رسند. یک نقشه CAD هاشور بخش خود را از دست می‌دهد و به‌عنوان fill های solid برمی‌گردد، چون رندرکننده الگو را به یک رنگ میانگین حل کرده و آن را رنگ زده. یا هاشور فرار می‌کند: یک بلوک عنوان که باید کاملاً سفید باشد خطوط قطری را از یک نمای جزئیات دو مسیر پیش‌تر می‌گیرد. هر دو مشکلات وضعیت الگو هستند، و فقط یکی از آن‌ها اصلاً درباره رسم tileهاست

چرا یک الگوی tiling روی مسیر بعدی نشت می‌کند؟

چون نام الگوی انتخاب‌شده بخشی از وضعیت گرافیکی است، نه یک ویژگی از operatorی که آن را استفاده کرده. ISO 32000-1 §8.6.6.2 یک color space الگو را به‌عنوان یکی تعریف می‌کند که مقدار رنگ آن یک نام الگو است که به scn یا SCN داده می‌شود، و هر مؤلفه دیگر وضعیت رنگ توسط q ذخیره و توسط Q بازیابی می‌شود. نام الگو باید همان قانون را دنبال کند. HotPDF آن را در رکورد وضعیت به‌عنوان FillPatternName و StrokePatternName نگه می‌دارد، در کنار خانواده color space fill و stroke، پس یک Q انتخاب قبلی را دقیقاً همان‌طور که CTM قبلی را برمی‌گرداند برمی‌گرداند

در عوض آن نام را در یک متغیر محلی داخل dispatcher operator ذخیره کنید، و از هر Q در stream جان سالم به در می‌برد. شکست سپس جایی غیرمنتظره ظاهر می‌شود: یک Form XObject رسم‌شده پس از مسیر الگودار انتخاب الگویی را به ارث می‌برد که content stream خودش هرگز نساخته، و fillهای آن هاشورخورده بیرون می‌آیند. formهای تودرتو آن را بدتر می‌کنند، چون هر سطح تودرتو وضعیتی را push و pop می‌کند که متغیر سرگردان نادیده می‌گیرد. تنظیم یک color space غیر-الگو با cs یا CS، یا صدور یک g / rg / k ساده، باید نام الگو را هم پاک کند، وگرنه انتخاب کهنه از color spaceای که به آن معنا داده جان سالم به در می‌برد

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

یک الگو از طریق یک clip رنگ می‌شود، هرگز به‌عنوان یک fill

مدل درست کاهشی (subtractive) است: clip دستگاه را به شکلی که رنگ می‌شود محدود کنید، سپس محتوای الگو را داخل آن اجرا کنید. HotPDF هرگز اول یک تقریب solid رسم نمی‌کند و رویش رنگ نمی‌زند، چون solid میانی از میان شکاف‌های بین tileها قابل‌مشاهده می‌بود و با هر transparency در محتوای tile می‌جنگید. §8.7.3.2 یک الگوی tiling را به‌عنوان یک content stream تکرارشده در بازه‌های افقی و عمودی ثابت توصیف می‌کند، و تکرار فقط در برابر یک clip که از قبل شکل درست را دارد معنا دارد. برای fillها تبدیل مستقیم است: HPDFSelectFillPathClip حالت polygon fill را برای f*، B* و b* روی ALTERNATE و برای نوع‌های nonzero روی WINDING تنظیم می‌کند، مسیر GDI را می‌سازد، و آن را با SelectClipPath در clip قطع می‌کند. آن یک خط چیزی است که باعث می‌شود یک fill الگودار even-odd همان سوراخ‌هایی را که یک fill solid even-odd می‌گذارد بگذارد، که دقیقاً همان چیزی است که یک ناحیه هاشورخورده دونات‌شکل نیاز دارد

strokeها بخشی هستند که به‌راحتی اشتباه می‌شوند. یک مسیر stroke شده هیچ داخلی ندارد، پس قطع کردن خود مسیر در clip یک ناحیه خالی می‌دهد و هیچ‌چیز رنگ نمی‌شود. HPDFSelectStrokePathClip بنابراین ابتدا یک قلم هندسی از وضعیت فعلی می‌سازد، با استفاده از PS_GEOMETRIC با end cap از J، join از j، محدودیت miter از M، و PS_USERSTYLE وقتی یک آرایه dash فعال است، سپس WidenPath را فراخوانی می‌کند تا outline stroke شده را به یک ناحیه قابل‌fill پیش از clip کردن تبدیل کند. رفتار cap، join، miter و dash روی یک مسیر الگودار-stroke شده سپس با یک stroke عادی از طریق ساخت مطابقت می‌کند نه از طریق یک پیاده‌سازی دوم. دو محدودیت صادقانه اینجا زندگی می‌کنند: عرض خطوط زیر یک واحد دستگاه به یک پیکسل clamp می‌شوند، و آرایه dash در شانزده entry بریده می‌شود، که سقفی است که ExtCreatePen می‌پذیرد

کدام tileها واقعاً قابل‌مشاهده‌اند؟

بازه قابل‌مشاهده از اجرای معکوس تبدیل می‌آید. جای‌گذاری tile در فضای الگو رخ می‌دهد، اما تنها چیزی که می‌داند چقدر از صفحه لمس می‌شود clip box دستگاه است، که در فضای دستگاه است. HotPDF BaseMatrix := CTM * PatternMatrix را می‌سازد، آن را معکوس می‌کند، و چهار گوشه clip box GDI را از طریق معکوس دوباره نگاشت می‌کند. مرزهای هم‌محور آن چهار گوشه نگاشت‌شده مستطیل فضای الگو را می‌دهند که ممکن است پوشش داده شود، و تقسیم آن مستطیل بر XStep و YStep در برابر BBox الگو بازه‌های اندیس بسته را می‌دهد. هر سلول سپس با یک CTM از CTM * PatternMatrix * Translate(i * XStep, j * YStep) رندر می‌شود، و بار دوم با چندضلعی BBox تبدیل‌شده خودش clip می‌شود. آن clip دوم وقتی XStep کوچک‌تر از عرض bounding box است اهمیت دارد، که همان روشی است که طراحی‌های tile همپوشان بیان می‌شوند؛ بدون آن، سلول‌های همسایه بیرون از حد اعلام‌شده خود روی هم رنگ می‌زدند. اگر clip به‌ازای هر سلول به‌عنوان NULLREGION برگردد، سلول بدون tokenize یا اجرای هیچ‌چیزی رد می‌شود

// 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 مشخص می‌کند یک الگوی بی‌رنگ فقط با یک color space الگو استفاده می‌شود که یک فضای زیرین اعلام می‌کند، پس scn ابتدا مقادیر مؤلفه را دریافت می‌کند و آخر نام الگو را. HotPDF آن مؤلفه‌ها را از طریق فضای زیرین ذخیره‌شده روی entry color space الگو حل می‌کند، که یعنی یک هاشور بی‌رنگ می‌تواند دقیقاً مانند هر fill دیگری با یک مرکب Separation یا یک ترکیب DeviceN رنگ‌آمیزی شود؛ مکانیزم آن حل‌کردن در رندر رنگ‌های spot از نوع Separation و DeviceN پوشش داده شده. داخل tile، دو نوع رنگ به‌شدت واگرا می‌شوند. برای PaintType 2 رندرکننده یک flag سرکوب color-operator برای مدت tile تنظیم می‌کند، پس هر g، rg، k یا scn در محتوای الگو نادیده گرفته می‌شود و هر علامت رنگ تأمین‌شده از بیرون را می‌گیرد. برای PaintType 1 عکس آن اعمال می‌شود: وضعیت fill و stroke به پیش‌فرض‌های PDF ریست می‌شود، DeviceGray مشکی با یک color space identity، و tile خودش را رنگ می‌کند. رد کردن آن ریست اجازه می‌دهد رنگی که اتفاقاً در عملگر f فعلی بوده به الگویی نشت کند که قرار بود خودتوصیف باشد

چرا عمق استک وضعیت گرافیکی باید پس از هر tile بازیابی شود؟

چون یک content stream الگو اجازه دارد نامتوازن باشد، و آسیب در سراسر سلول‌ها ترکیب می‌شود. یک tile که stream آن سه عملگر q و دو عملگر Q دارد استک را یک فریم عمیق‌تر از جایی که شروع شده رها می‌کند. فقط رکورد وضعیت فعلی بین سلول‌ها بازیابی کنید و عمق همچنان رشد می‌کند، پس سلول شماره دویست از یک فریم استک اجرا می‌شود که متعلق به سلول شماره صد و نود و نه است، با هر CTM و clip که آن فریم حمل می‌کرد. HotPDF بنابراین پیش از حلقه tile یک snapshot از رکورد وضعیت و عمق استک می‌گیرد و در بالای هر تکرار RestoreSnapshot را فراخوانی می‌کند، که استک را به طول ذخیره‌شده بریده و وضعیت ذخیره‌شده را در یک گام دوباره نصب می‌کند. دیکشنری Resources صفحه و flag سرکوب color-operator در همان مرز بازیابی می‌شوند، چون یک tile ممکن است منابع خودش را ارجاع دهد و نباید آن‌ها را به همسایه‌اش بدهد. وضعیت clip GDI همان رفتار را از طریق یک جفت SaveDC / RestoreDC دور هر سلول می‌گیرد، پس یک tile که clip W n خودش را نصب می‌کند نمی‌تواند ناحیه در دسترس سلول بعدی را کوچک کند

بودجه‌ها، امتناع‌ها، و چیزی که رندرکننده نمی‌کشد

الگوهای tiling راحت‌ترین جای یک PDF برای نوشتن یک فایل denial-of-service هستند، پس محدودیت‌ها اعداد سخت‌اند نه هیوریستیک. تودرتوسازی الگو به عمق ۴ سقف می‌شود، همان محافظی که برای بازگشت Form XObject استفاده می‌شود، که یک الگو را که خودش را از طریق دیکشنری منابع خودش ارجاع می‌دهد متوقف می‌کند. یک fill مسیر واحد ممکن است در مجموع حداکثر ۱۶٬۳۸۴ tile اجرا کند، شمرده‌شده در سراسر الگوهای تودرتو و ریست‌شده فقط وقتی رنگ‌کردن بیرونی‌ترین الگو شروع شود. یک grid tile که تعداد سلول برنامه‌ریزی‌شده آن از آنچه از آن بودجه باقی مانده فراتر رود، کاملاً رد می‌شود، پیش از اجرای یک سلول واحد

هندسه تباهیده به‌جای تقریب رد می‌شود. یک BBox گمشده یا ناحیه‌صفر، یک XStep یا YStep که مقدارش زیر 1e-6 است، یک حاصل‌ضرب CTM * PatternMatrix بدون معکوس، مختصات clip نگاشت‌شده فراتر از 1e9، یا یک اندازه اندیس فراتر از یک میلیون همه باعث می‌شوند رنگ‌کردن الگو بدون رسم برگردد. نتیجه یک ناحیه رنگ‌نشده است به‌جای یک thread رندر آویزان، که همان مبادله‌ای است که در یک مبدل batch می‌خواهید. عملکرد از یک تصمیم می‌آید: stream الگو یک‌بار به‌ازای هر رنگ‌کردن با HPDFTokenizeContentStream tokenize می‌شود و آرایه token در سراسر هر سلول قابل‌مشاهده دوباره استفاده می‌شود، پس تعداد tile هزینه اجرا را ضرب می‌کند اما هرگز هزینه lexing را

رندر یک صفحه الگودار از Delphi

هیچ‌چیز درباره پشتیبانی الگو کد فراخوانی را تغییر نمی‌دهد. سند را بارگذاری کنید، یک صفحه درخواست کنید، و کار tiling داخل مفسر content-stream‌ای رخ می‌دهد که رندر page-to-bitmap از قبل آن را می‌راند. همان مفسر context دستگاه bitmap، metafile و printer را تغذیه می‌کند، پس یک نقشه هاشورخورده که در یک thumbnail preview درست به‌نظر می‌رسد با همان هندسه tile چاپ می‌شود. الگوهای shading از نوع PatternType 2 یک شاخه متفاوت می‌گیرند که مسیر ارزیابی خود را با عملگر ساده sh به اشتراک می‌گذارد، شرح‌داده‌شده با جزئیات تحت رندر shadingهای axial و radial

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 را برای مقادیر تباهیده بازرسی کنید، یا تعداد tileهایی که grid نیاز دارد را در برابر سقف ۱۶٬۳۸۴ بشمارید. یک ناحیه که در یک رنگ تخت واحد رنگ شده یعنی نام الگو هرگز به عملگر رنگ‌کردن نرسیده، که به ترتیب cs و scn در stream اشاره می‌کند. یک الگو که جایی ظاهر می‌شود که به آن تعلق ندارد یعنی بازیابی وضعیت، و جایی که باید نگاه کنید مدیریت q / Q اطراف form یا مسیری است که آن را به ارث برده

الگوهای tiling یکی از آن ویژگی‌های PDF هستند که تا وقتی فایلی که به آن‌ها نیاز دارد در inbox شما فرود بیاید نامرئی می‌مانند، و سپس کل کار می‌شوند. اگر دارید viewerهای نقشه، مبدل‌های اسناد مهندسی یا رندرکننده‌های گزارش را روی Delphi یا C++Builder می‌سازید، کامپوننت کامل و API رندر آن روی صفحه کامپوننت HotPDF Delphi PDF مستند شده