HotPDF با یک فراخوانی، BuildLoadedPageSVG، صفحهای از هر سند PDF بارگذاریشده را به نشانهگذاری SVG مستقل صادر میکند که سند کامل SVG را بهصورت رشته برمیگرداند. نشانهگذاری صادرشده هندسه صفحه، متن را بهصورت عناصر واقعی text در SVG، تصاویر رستری تعبیهشده، و وضعیت قلم (stroke state) که عملگرهای PDF در هر عملیات ترسیم برقرار کرده بودند را حمل میکند
همین بخش آخر جایی است که بیشتر مبدلهای خانگی بهآرامی از هم میپاشند. تبدیل یک صفحه PDF به SVG در نگاه اول یک مسئله مختصات به نظر میرسد، اما در عمل یک مسئله وضعیت (state) است. PDF یک ماشین پشتهای است که وضعیت گرافیکی آن هنگام تفسیر جریان محتوا تغییر میکند؛ SVG یک درخت اعلانی است که هر عنصر آن ویژگیهای نمایشی خودش را حمل میکند. هر چیزی که مفسر در لحظه انتشار یک عنصر نتواند از آن نماگرفت (snapshot) بگیرد، بهسادگی از خروجی حذف میشود، و این خطا خاموش است: نتیجه یک SVG معتبر است که صفحهای اندکی نادرست را نمایش میدهد
چرا یک صفحه PDF بهسادگی به SVG تبدیل نمیشود؟
سه ناسازگاری این تبدیل را غیرپیشپاافتاده میکنند، و هر سه، خروجیای تولید میکنند که تا زمانی که آن را کنار نسخه اصلی مقایسه نکنید، قابلقبول به نظر میرسد. اولی محور y است. فضای کاربر PDF از گوشه پایینچپ صفحه بهسمت بالا رشد میکند؛ SVG از گوشه بالا-چپ بهسمت پایین رشد میکند. یک وارونگی (flip) واحد در سطح صفحه مختصات ترسیم را درست میکند اما سپس هر گلیف را خراب میکند، زیرا وارونهکردن کل بوم شکل حروف را نیز آینهای میکند
ناسازگاری دوم ارثبری است. در PDF، q و Q یک وضعیت گرافیکی را که شامل عرض خط، سربرگ خط (line cap)، اتصال خط (line join)، حد میتر (miter limit)، آرایه خطچین، فاز خطچین و آلفا میشود، push و pop میکنند. در SVG، عنصری که ویژگیای را نام نبرده باشد آن را از یک گروه اجدادی به ارث میبرد، که قاعده دامنهبندی کاملاً متفاوتی است. یک صادرکننده که فقط ماتریس تبدیل جاری را دنبال میکند و وضعیت قلم را فراموش میکند، اجازه میدهد وضعیت بازگردانیشده پس از یک Q به عناصر بعدی نشت کند
سومی این است که PDF چندین چیز را با قرارداد بیان میکند نه با مقدار. سربرگها و اتصالهای خط اعداد صحیح هستند، عرض خط صفر بهمعنای یک خط موئی (hairline) در فضای دستگاه است نه یک خط نامرئی، و نسخههای ستارهدار عملگرهای رنگآمیزی بهجای رنگ، قاعده پیچش (winding rule) را تغییر میدهند. هر یک از اینها به یک ترجمه نیاز دارد، نه یک رونوشت
یک فراخوانی برای حالت متداول
برای کار معمول صادرکردن صفحات بهمنظور یک نمایشگر وب، ابزار دیف (diffing) یا تحویل طراحی، سطح API فقط یک تابع است. BuildLoadedPageSVG یک اندیس صفحه صفر-پایه را نسبت به سند بارگذاریشده فعلی میگیرد و سند SVG را بهصورت AnsiString برمیگرداند:
var
Pdf: THotPDF;
I: Integer;
Svg: AnsiString;
Output: TFileStream;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('statements.pdf', '') <= 0 then
Exit; // LoadFromFile تعداد صفحات را برمیگرداند
for I := 0 to Pdf.LoadedPageCount - 1 do
begin
Svg := Pdf.BuildLoadedPageSVG(I);
if Length(Svg) = 0 then
Continue;
Output := TFileStream.Create(Format('page-%d.svg', [I + 1]), fmCreate);
try
Output.WriteBuffer(Svg[1], Length(Svg));
finally
Output.Free;
end;
end;
finally
Pdf.Free;
end;
end;
همین قابلیت صادرات توسط ابزار خطفرمان HotPDF بهصورت دستور export-svg در دسترس است، که در خطلولههای ساخت (build pipeline) و اسکریپتهای رگرسیون که در آنها بدون نوشتن هیچ کد پاسکالی به یک نمایش متنیِ قابلدیف از صفحه نیاز دارید، مفید است. از آنجا که SVG متن است، همراه طبیعی مسیر رستری است که در رندر یک صفحه PDF به بیتمپ شرح داده شده: بیتمپ میگوید صفحه چه شکلی است، SVG میگوید از چه چیزی ساخته شده
متن PDF چگونه به عناصر متنی SVG نگاشت میشود؟
HotPDF زنجیره ماتریس متن را بهصورت پیشوند ضرب در CTM ضرب در ماتریس متن ضرب در وارونگی گلیف میسازد، که در آن وارونگی گلیف یک ضرب از سمت راست در matrix(1,0,0,-1,0,0) است. این عامل سمتراست تنها برای خنثیکردن وارونگی عمودی سطح صفحه برای شکل گلیفها وجود دارد، زیرا در غیر این صورت متن SVG که در یک قاب محلی وارونه ترسیم شده باشد وارونه به نظر میرسد. قراردادن این تصحیح در ماتریس بهجای کدِ ویژه، به این معناست که متن چرخاندهشده، آینهای و برشی (sheared) همگی بدون شاخههای اضافی درست از آب درمیآیند
چیدمان افقی از نحو چندمقداریِ x در عنصر text در SVG استفاده میکند، یک مختصات به ازای هر نویسه، که از پیشروی هر گلیف بهعلاوه فاصلهگذاری نویسه Tc و فاصلهگذاری واژه Tw که در آن لحظه اعمال بودهاند، انباشته میشود. مقیاسگذاری افقی Tz بهجای آنکه جداگانه منتشر شود، در ستونهای a و c ماتریس متن تا میشود، بنابراین نمایشگری که ویژگیهای متنی غیرمتعارف را نادیده میگیرد همچنان هر گلیف را همانجایی که PDF گذاشته بود، قرار میدهد. متنی که از طریق شکلدهی پیچیده (complex shaping) تولید شده، که در شکلدهی متن اسکریپتهای پیچیده پوشش داده شده، همین مسیر را طی میکند، زیرا شکلدهنده (shaper) تا زمانی که جریان محتوا تفسیر میشود، خوشهها را از قبل به گلیفهای جایگذاریشده حل کرده است
چرخش و تصاویر: دو وارونگی که بهآسانی برعکس میشوند
صفحهای با ورودی /Rotate ناصفر به یک پیشتبدیل نیاز دارد که از یک وارونگی نسبت به ارتفاع بوم چرخاندهشده و یک چرخش بیانشده در فضای نمایش y-رو-به-بالا ترکیب شده باشد. سه ماتریس چرخش عبارتاند از (0,-1,1,0,0,W) برای ۹۰ درجه، (-1,0,0,-1,W,H) برای ۱۸۰ درجه و (0,1,-1,0,H,0) برای ۲۷۰ درجه، که W و H ابعاد صفحه پیش از چرخش هستند. استخراج دستیِ اینها دقیقاً در سه نقطه باعث بروز خطای علامت میشود، بنابراین صادرکننده آنها را از طریق همان روال ضرب ماتریسی که هر تبدیل دیگری را نیز مدیریت میکند، میسازد
تصاویر تعبیهشده به وارونگی مخصوص به خود نیاز دارند، زیرا فضای تصویر PDF اولین ردیف نمونه را در لبه بالایی مربع واحد قرار میدهد، درحالیکه عنصر image در SVG یک قاب محلی y-رو-به-پایین دارد. بنابراین تبدیل منتشرشده، CTM ضربشده از سمت راست در matrix(1,0,0,-1,0,1) است. اشتباهکردن در این مورد عکسهایی عمودی-آینهشده روی یک صفحه در غیر این صورت بینقص تولید میکند، که نوعی نقص است که یک بازبین فوراً آن را میبیند اما یک آزمون خودکار اغلب متوجه آن نمیشود
دستگاه وضعیت گرافیکی واقعاً چه چیزی را حفظ میکند؟
HotPDF عملگرهای وضعیت قلم w، J، j، M و d را از طریق یک رابط دستگاه اختیاری جداگانه ارسال میکند، بنابراین وفاداری قلم بدون تغییر vtable دستگاه محتوای موجود و بدون شکستن سازگاری دودویی برای کدی که با نسخههای قدیمیتر ساخته شده، افزوده شد. بهطور مشخص، SVG صادرشده بهجای اعداد صحیح خام PDF، کلیدواژههای ترجمهشده دریافت میکند:
// شمارشهای عددی PDF به ویژگیهای کلیدواژهای SVG تبدیل میشوند
// line cap 0, 1, 2 -> butt, round, square
// line join 0, 1, 2 -> miter, round, bevel
//
// عرض خط صفر در PDF بهمعنای یک خط موئی در فضای دستگاه است، بنابراین
// صادرکننده vector-effect="non-scaling-stroke" را منتشر میکند تا خط
// پس از CTM قابلمشاهده و نزدیک به یک پیکسل دستگاه باقی بماند
//
// f* B* b* قاعده even-odd را انتخاب کرده و fill-rule="evenodd" را منتشر میکنند،
// درحالیکه f B b پیشفرض SVG یعنی پیچش nonzero را حفظ میکنند
بازگردانی وضعیت در یک Q، شفافیت، عرض خط، سربرگ، اتصال، حد میتر، آرایه خطچین و فاز خطچین را با هم پوشش میدهد. Form XObjectهای تودرتو همین مجموعه کامل را در مرزهای خود نماگرفت و بازگردانی میکنند، بنابراین یک حاشیه خطچین تعریفشده درون یک مُهر نمیتواند الگوی خود را به محتوای صفحه بعدی نشت دهد. اگر به دلایل دیگری رفتار clipping و CTM را از قبل دنبال میکنید، این همان مدل وضعیتی است که در درونریزی برداری EMF و WMF ظاهر میشود، با این تفاوت که در جهت مخالف اجرا میشود
مرزهایی که ارزش دانستن پیش از عرضه را دارند
صادرکننده در مورد دامنه خود صادق است، و شناختن مرزهای آن از پیش، ارزانتر از کشفکردن آنها در تولید است. رنگ از طریق عملگرهای rg، RG، g و G به دستگاه SVG میرسد. پرکردنهایی که از طریق یک فضای رنگی بهعلاوه scn برقرار شدهاند، که همان روش رنگآمیزی رنگهای Separation، DeviceN و ICCBased است، بهصورت یک سهتایی RGB حلشده به دستگاه نمیرسند، بنابراین صفحاتی که رنگهای spot را به این شکل استفاده میکنند، هندسه خود را صادر میکنند اما آن رنگها را نه. برای منابع چاپمحور، بهجای آن رستری کنید یا ابتدا رنگهای spot را مسطح (flatten) کنید؛ خود مدل رنگآمیزی در رندر رنگهای spot از نوع Separation و DeviceN پوشش داده شده
دو نکته کوچکتر در زمان اشکالزدایی صرفهجویی میکنند. مقادیر لفظی رنگ هگزادسیمال با حروف بزرگ منتشر میشوند، بنابراین آزمونی که #ff0000 را تأیید میکند در برابر #FF0000 کاملاً درست شکست میخورد. و دستگاه SVG از طریق رابط خود شمارش ارجاع (reference counted) میشود، به این معنا که آزادسازی آن به این است که اجازه دهید رابط از دامنه خارج شود نه اینکه Free را روی شیء فراخوانی کنید، تمایزی که در صورتی اهمیت دارد که دستگاه را برای انتشار نشانهگذاری خودتان در کنار محتوای صفحه گسترش دهید
صادرات SVG بهطور طبیعی با مقایسه ساختاری همراه میشود، وقتی که نیاز دارید بدانید آیا سندی تولیدشده واقعاً بین دو ساخت تغییر کرده یا نه. مجموعهابزار گستردهتر پیرامون اسناد بارگذاریشده، از رندر گرفته تا ویرایش تا صادرات، در صفحه مؤلفه HotPDF PDF برای دلفی مستند شده