سندی را از Microsoft Word یا Excel با Save as PDF خروجی بگیرید و فایلی که روی دیسک مینشیند، بیشتر اوقات یک فایل مرجع ترکیبی است. این فایل اطلاعات ارجاع متقاطع خود را دو بار حمل میکند: یک بار به شکل همان جدول کلاسیک با عرض ثابت که تا نسخهٔ 1.4 پایان هر PDF بود، و یک بار به شکل یک استریم ارجاع متقاطع فشرده که بخش عمدهٔ سند در عمل به آن وابسته است. یک کلید تنها در تریلر، /XRefStm، این دو نما را به هم میدوزد، و اینکه یک ابزار کل سند را ببیند یا نه، به این برمیگردد که آن کلید را دنبال کند یا نکند
این مقاله فایلهای ترکیبی را از سمت مصرفکننده بررسی میکند: بایتهای انتهای فایل چه شکلی دارند، دو نما زیر فشار ویرایش چگونه از هم فاصله میگیرند، و یک خط پردازش دلفی چگونه میتواند ورودیهای ترکیبی را تشخیص دهد و مسیردهی کند. اینکه یک بارگذار این دو نما را چگونه ادغام میکند و چرا ترتیب این کار قابل مذاکره نیست، موضوع مقالهٔ HotPDF ما دربارهٔ بارگذاری فایلهای مرجع ترکیبی است؛ این یکی دربارهٔ بازشناختن خود این چیدمان است
چرا خروجیهای آفیس ایندکس را دو بار مینویسند
نسخهٔ PDF 1.5 دو قابلیت را معرفی کرد که شکل فایل را تغییر داد: استریمهای ارجاع متقاطع که ایندکس اشیا را بهجای یک جدول متن ساده به شکل دادهٔ باینری فشرده ذخیره میکنند، و استریمهای شیء که اشیای کوچک بسیاری را در یک ظرف فشردهشده با Flate بستهبندی میکنند. نویسندهای که از آنها استفاده کند فایلهای کوچکتری تولید میکند، اما خوانندهٔ PDF 1.4 نمیتواند نتیجه را باز کند، چون ساختارهایی که به آنها تکیه دارد، یعنی کلمهٔ کلیدی xref و دیکشنری trailer، دیگر آنجا نیستند
استاندارد ISO 32000-1 §7.5.8.4 مصالحه را تعریف میکند. یک فایل مرجع ترکیبی هر دو را مینویسد: یک جدول ارجاع متقاطع کلاسیک که نشانی اشیایی را میدهد که خوانندهٔ قدیمی باید به آنها برسد، از جمله کاتالوگ و درخت صفحهها، و یک استریم ارجاع متقاطع که هر چیز دیگری را ایندکس میکند. اشیایی که درون استریمهای شیء تا خوردهاند در جدول کلاسیک آزاد علامت میخورند، پس خوانندهٔ 1.4 بدون شکایت از آنها میگذرد؛ جای واقعی آنها فقط در استریم وجود دارد. تریلر کلاسیک آنگاه کلید /XRefStm را حمل میکند که افست بایتی همان استریم را نگه میدارد. نمایشگر قدیمی هرگز آن کلید را نمیخواند و فایل را از روی نمای جدول رندر میکند. نمایشگر مدرن آن را دنبال میکند و سند کامل را میبیند. Word و Excel سالهاست دقیقاً همین چیدمان را بیرون میدهند، و به همین دلیل فایلهای ترکیبی یک گوشهٔ نادر نیستند بلکه سهم بزرگی از آنچه خطوط پردازش سازمانی دریافت میکنند به شمار میروند
انتهای یک فایل ترکیبی چه شکلی است
فهم این چیدمان از روی بایتها سادهترین است. اینجا انتهای یک فایل ترکیبی کوچک آمده و افستها کوتاه شدهاند؛ در یک خروجی واقعی آفیس، مقدار /XRefStm معمولاً افستی بزرگ نزدیک انتهای فایل است. ترتیب خواندن همان پیمایش از انتها به ابتداست که در مرور ما بر ساختار فایل PDF شرح داده شده است: یافتن %%EOF، خواندن startxref و پریدن به جدول
% ... اشیای بدنه، شامل استریمهای شیء و، در بایت 116،
% استریم ارجاع متقاطع (یک شیء استریم با /Type /XRef) ...
xref % بخش کلاسیک: چیزی که startxref به آن اشاره میکند
0 4
0000000000 65535 f % خانهٔ 0: سر فهرست آزاد، همیشه حاضر
0000000017 00000 n % شیء 1: کاتالوگ، برای هر خوانندهای دیدنی
0000000000 65535 f % شیء 2: آزاد علامت خورده -- درون یک استریم شیء است
0000000000 65535 f % شیء 3: همان؛ فقط نمای استریم جای آن را مییابد
trailer
<<
/Size 4
/Root 1 0 R
/XRefStm 116 % افست بایتی استریم ارجاع متقاطع
>>
startxref
7164 % افست بایتی کلمهٔ کلیدی 'xref' بالا
%%EOF
دو جزئیات در این دامپ کل سازوکار را حمل میکنند. نخست، startxref عمداً به بخش کلاسیک اشاره میکند: این همان نشانی است که خوانندهٔ قدیمی باید روی آن فرود بیاید. استریم ارجاع متقاطع تنها از راه کلید /XRefStm درون دیکشنری تریلر قابل دسترسی است، پس تجزیهگری که هرگز دنبال آن کلید نگردد هرگز نمیفهمد چنین استریمی وجود دارد. دوم، اشیای 2 و 3 دروغهایی از نوع بیآزار هستند. جدول کلاسیک آنها را آزاد اعلام میکند، اما آنها اشیای واقعیای هستند که درون یک ظرف فشرده نشستهاند؛ علامت آزاد همان چیزی است که نمیگذارد خوانندهٔ 1.4 روی مدخلهایی که نمیتواند به کار ببرد سکندری بخورد. مصرفکنندهای که فقط به نمای کلاسیک اعتماد کند نتیجه میگیرد بیشتر این سند اصلاً وجود ندارد
دو نما چگونه از هم فاصله میگیرند
فایل ترکیبی که تازه از Word بیرون آمده از درون سازگار است: هر دو نما همان سند را توصیف میکنند، هر کدام در دامنهٔ اعلامشدهٔ خود. دردسر وقتی شروع میشود که فایل به دست ابزاری ویرایش شود که تنها یکی از دو نما را میفهمد. یک ابزار مهرزنی را در نظر بگیرید که یک بهروزرسانی افزایشی به سبک کلاسیک الحاق میکند: اشیای تازه، یک بخش xref جدید، یک زنجیرهٔ /Prev به بخش پیشین و یک تریلر تازه. اگر آن تریلر کلید /XRefStm را بیندازد، نمای استریم بیصاحب میشود؛ اگر مقدار قدیمی را همانطور جلو ببرد، نمای استریم همچنان سند را همانگونه توصیف میکند که پیش از ویرایش بود. در هر دو حالت، اکنون دو ایندکس دربارهٔ محتوای فایل با هم اختلاف دارند
فایل حاصل امضای شکست ویژهای دارد: اشیایی که در یک نما دیده میشوند در نمای دیگر غایب یا کهنهاند. خوانندهای که از راه نمای استریم ارجاع را حل میکند، نسخهٔ پیش از ویرایش یک شیء بهروزشده را مییابد، یا برای شیء الحاقشده اصلاً مدخلی پیدا نمیکند. خوانندهٔ نمای جدول ویرایش را میبیند اما رد اشیای فشردهای را که فقط استریم جایشان را میداند گم میکند. در عمل این وضع به شکل فیلدهای فرمی بروز میکند که در یک نمایشگر میمانند و در دیگری ناپدید میشوند، حاشیهنویسیهایی که گویا یک گذر مهرزنی آنها را حذف کرده، یا جستوجوهایی که کاملاً روی شیء اشتباه فرود میآیند
چیزی که اشکالزدایی این فایلها را گران میکند این است که Adobe Acrobat معمولاً بدون شکایت آنها را باز میکند: وقتی ایندکس با بایتها نمیخواند، بیسروصدا با پویش سرآیندهای شیء دادهٔ ارجاع متقاطع را از نو میسازد، پس هر کسی که فایل خراب را تولید کرده چیز نادرستی نمیبیند. شکست بعدتر رخ مینماید، وقتی فایل به یک مصرفکنندهٔ سختگیر میرسد، یک اعتبارسنج preflight، یک سرویس امضا، یک کار ورود به بایگانی، که به ساختار اعلامشده اعتماد میکند و از اشیای گمشده یا ناسازگاری ارجاع متقاطع گزارش میدهد. جملهٔ «در Acrobat خوب باز میشود» تقریباً آغاز هر تیکت ناهمگامی ترکیبی است
تشخیص یک فایل ترکیبی با دلفی خالص
دستهبندی ورودیها به کتابخانهٔ PDF نیاز ندارد. کلید /XRefStm تنها میتواند درون یک دیکشنری تریلر کلاسیک ظاهر شود، و تریلر فعال در چند کیلوبایت پایانی فایل مینشیند، چون مشخصات ایجاب میکند %%EOF نزدیک انتهای فیزیکی بیاید. خواندن یک پنجرهٔ کراندار از انتها و جستوجو در آن برای تریاژ کافی است:
uses
System.SysUtils, System.Classes, System.StrUtils, System.Math;
function IsHybridReferencePdf(const FileName: string): Boolean;
const
TailWindow = 2048;
var
Stream: TFileStream;
Buf: TBytes;
Tail: string;
Len, TrailerPos, NextPos, KeyPos, StartXrefPos: Integer;
begin
Result := False;
Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
if Stream.Size < 48 then
Exit;
Len := Min(TailWindow, Integer(Stream.Size));
SetLength(Buf, Len);
Stream.Position := Stream.Size - Len;
Stream.ReadBuffer(Buf[0], Len);
finally
Stream.Free;
end;
// هر کلمهٔ کلیدی دخیل، ASCII هفتبیتی است، پس رمزگشایی بایتبهبایت امن است
Tail := TEncoding.ANSI.GetString(Buf);
// آخرین کلمهٔ کلیدی 'trailer' را بیابید: با بهروزرسانیهای افزایشی،
// جدیدترین تریلر همان است که فایل را اداره میکند
TrailerPos := 0;
NextPos := Pos('trailer', Tail);
while NextPos > 0 do
begin
TrailerPos := NextPos;
NextPos := PosEx('trailer', Tail, NextPos + 1);
end;
if TrailerPos = 0 then
Exit; // بدون تریلر کلاسیک: فایلی تماماً مبتنی بر استریم xref، نه ترکیبی
// تریلر ترکیبی کلید /XRefStm را میان 'trailer' و 'startxref' حمل میکند
KeyPos := PosEx('/XRefStm', Tail, TrailerPos);
StartXrefPos := PosEx('startxref', Tail, TrailerPos);
Result := (KeyPos > 0) and
((StartXrefPos = 0) or (KeyPos < StartXrefPos));
end;
سه نتیجهٔ ممکن دقیقاً با سه چیدمان میخوانند. فایل صرفاً کلاسیک تریلر دارد اما /XRefStm ندارد: False. فایلی که تمامقد به استریمهای ارجاع متقاطع متعهد شده اصلاً کلمهٔ کلیدی trailer ندارد، چون کلیدهای تریلرش درون دیکشنری استریم زندگی میکنند: آن هم بهدرستی False، چون چنین فایلی فشرده است، نه ترکیبی. تنها چیدمان دو ایندکسه True برمیگرداند
برای کاربرد تولیدی، دو سختسازی ارزش خطوط اضافه را دارند. عدد صحیح پس از /XRefStm را تجزیه کنید، به آن افست بروید و تأیید کنید که واقعاً یک شیء استریم با /Type /XRef آنجا نشسته است؛ فایل بریدهشده میتواند کلید را داشته باشد در حالی که استریم رفته است، و این در سطل دیگری غیر از یک ترکیبی سالم جای میگیرد. و اندازهٔ پنجره را یک پارامتر بدانید: دو کیلوبایت خروجی معمول آفیس را پوشش میدهد، اما یک دیکشنری تریلر بهطور غیرعادی بزرگ میتواند کلمهٔ کلیدی را از برد بیرون براند، و پهنتر کردن پنجره بهتر از آن است که فایل را بهاشتباه کلاسیک اعلام کنید
مسیردهی فایلهای ترکیبی در یک خط پردازش دلفی
تشخیص، یک تصمیم مسیردهی برایتان میخرد. برای فایلهایی که فقط خوانده، رندر یا اعتبارسنجی میشوند، از بارگذاری استفاده کنید که هر دو نما را حل میکند، سپس رفتار را بسنجید نه بایتها را. PDFium Component زنجیرهٔ /XRefStm را هنگام بارگذاری تجزیه میکند، پس جدول اشیایی که کد شما میبیند همان جدول ادغامشده است، و بررسیهای شرحدادهشده در مقالهٔ ما دربارهٔ اعتبارسنجی استریمهای شیء و ارجاع متقاطع بدون تغییر برقرارند. اگر یک ترکیبی ناهمگام آنقدر آسیب دیده باشد که از بارگذاری سر باز زند، موتور آن را از راه مجموعهٔ خطاهایش گزارش میدهد، یعنی FPDF_ERR_SUCCESS، FPDF_ERR_UNKNOWN، FPDF_ERR_FILE، FPDF_ERR_FORMAT، FPDF_ERR_PASSWORD، FPDF_ERR_SECURITY و FPDF_ERR_PAGE، که از میانشان FPDF_ERR_FORMAT همان چیزی است که آسیب ساختاری تولید میکند. با این حال به آن نشانه تکیه نکنید: PDFium بنا بر طراحی سهلگیر است و بیشتر فایلهای ناسازگار را بیصدا بازسازی میکند، پس بارگذاری موفق ثابت میکند فایل قابل بازیابی بوده، نه اینکه دو نمای آن با هم میخوانند. بررسی سازگاری معنادار، مقایسهٔ آن چیزی است که یک پیمایش کامل اشیا مییابد با آنچه /Size تریلر اعلام میکند
برای فایلهایی که خط پردازش شما تغییرشان میدهد، امنترین سیاست آن است که اصلاً نگذارید ترکیبی بمانند. یک بارگذاری و سپس ذخیرهٔ کامل با HotPDF سند را با یک ارجاع متقاطع یگانه و خودسازگار در یک قالب بازنویسی میکند: بدون /XRefStm، بدون نمای دومی که از همگامی خارج شود، با هر شیء متعلق به دقیقاً یک مدخل ایندکس. همین عادیسازی است که پیش از ورود به بایگانی، پیش از یک RIP یا سرویس امضای سختگیر در پاییندست، و پس از هر ویرایشی که روی ورودی ترکیبی اعمال شده میخواهید. کار میکند چون بارگذار در مسیر ورود دو نما را درست ادغام کرده است، همان سازوکاری که مقالهٔ مرجع ترکیبی HotPDF با جزئیات آن را میپیماید
تنها دستهای از فایلها که باید به حال خود رها شوند اسناد امضاشدهٔ دیجیتال هستند. بازنویسی کامل هر بایت را جابهجا میکند، و این هر امضایی را که روی بازههای اصلی محاسبه شده بیاعتبار میسازد. تغییر در یک ترکیبی امضاشده باید به شکل یک بهروزرسانی افزایشی درست وارد شود که هر دو نما را نگه میدارد؛ فایلی که فقط به خواندن نیاز دارد باید دستنخورده عبور کند. عادیسازی برای فایلهایی است که مالکشان هستید؛ به فایلهای امضاشده فقط الحاق میکنید
PDFهای مرجع ترکیبی بدشکل نیستند؛ آنها پل سازگاری خود قالباند، و برنامههای آفیس تا زمانی که خوانندگان PDF 1.4 در پایگاه نصب زنده باشند به تولیدشان ادامه میدهند. خط پردازشی که بتواند کلید /XRefStm را ببیند، سند ادغامشده را با PDFium Component اعتبارسنجی کند و خروجی تمیز تکایندکسه را با HotPDF Delphi Component بازتولید کند، با آنها همانطور رفتار میکند که هستند: ورودیهای معمولی با یک تابلوی راهنمای اضافی در تریلر