PDFlibPas (PDF Library for Delphi), bir dosyayı yazmadan önce her nesneyi bir PDF sürüm kuralı tablosuyla karşılaştırır ve bu PDF sürüm preflight'ı yakın zamana kadar sıradan CAD ölçüm sözlüklerini jeouzaysal sanıyordu. Tek sayfalık bir CAD çizimi sorunsuz yükleniyor, sonra SaveToFile 0 döndürüyor, LastErrorCode 602 oluyor ve 1.7 ExtensionLevel 3 istiyordu. Düzeltilmiş kurallar, doğrusal /Measure sözlüklerini (/Subtype /RL) sıradan PDF 1.6 sayar ve extension kapısını gerçek jeouzaysal işaretlere ayırır
Dosya corpus kabulünden geldi: tek sayfa, tek bir optional-content grubu, iki doğrusal ölçüm viewport'u; bir mimari CAD paketinin, görüntüleyicinin kattan mesafe okuyabilmesi için yazdığı türden çıktı. Hiçbir yanı egzotik değildi; reddin önemi tam olarak buydu. Geçerli bir dosyayı engelleyen preflight, yavaş olandan da kötüdür, çünkü çağıran belgenin içermediği bir özelliği işaret eden, kendinden emin görünen bir tanı alır. Düzeltme iki parça aldı: bir kuralın ardındaki spesifikasyon okuması ve kuralın baktığı düzeyde iki sözlük türünü ayırt edemediğinin fark edilmesi
PDFlibPas save sırasındaki sürüm preflight'ı nasıl çalışır?
Save kapısı olan PrepareAndCheckSaveVersion, her dolaylı nesneyi PDFFeatureRules ile karşılaştırır ve hem eşleşen hem de hedefin izlediğinden fazlasını isteyen ilk kuralda başarısız olur. Hedef, belge sürümü (LockSaveVersion ile sabitlenen sürüm ya da) artı /Extensions /ADBE altında bildirilen Adobe extension seviyesidir. Her TPDFFeatureRule kaydı bir MinVersion, bir MinExtensionLevel, fmkDictKey ya da fmkDictSubtype gibi bir MatchKind, bir Match dizisi, insan-okur bir Feature adı ve isteğe bağlı bir callback taşır. AddRule düz bir sürüm kuralı kaydeder; AddExtensionRule her zaman MinVersionu 17'de sabitler ve üzerine bir extension seviyesi ekler, böylece bir extension kuralı yalnızca PDF 1.7 artı doğru /Extensions girdisiyle tatmin edilebilir. Kapı tetiklendiğinde istenen sürüm ve özellik adı çağıran için saklanır ve GetInformation 311, 312 ve 313 anahtarları onları ortaya koyar
var
Pdf: TPDFlib;
begin
Pdf := TPDFlib.Create;
try
if Pdf.LoadFromFile('floor-plan.pdf', '') <> 1 then
raise Exception.Create('load failed');
if Pdf.SaveToFile('floor-plan-out.pdf') <> 1 then
if Pdf.LastErrorCode = PDFLIB_ERROR_VERSION_COMPLIANCE then
// 311: istenen sürüm, 312: tetikleyen özellik,
// 313: save hedefinin kilitli olduğu sürüm (kilitsizken '')
Writeln('Needs ', Pdf.GetInformation(311),
' for ', Pdf.GetInformation(312),
', locked at [', Pdf.GetInformation(313), ']');
finally
Pdf.Free;
end;
end;
Sıradan bir CAD çizimi 602 hatasıyla neden başarısız oldu?
Kural tablosunda AddExtensionRule(3, fmkDictKey, 'Measure', '/Measure geospatial dictionary', Nil) vardı; bu, salt /Measure anahtarı taşıyan her sözlükte tetikleniyordu ve her ölçüm viewport'u bir tane taşır. Sayfanın /VP dizisi viewport sözlükleri tutar, her viewport ölçüm sözlüğünü /Measure üzerinden işaret eder ve anahtar varlığı eşleşmesi ölçüm sözlüğünün gerçekte ne olduğuna bakmadan orada duruyordu. Yükleme sırasındaki özellik taraması belge sürüm numarasını 1.7'ye çıkarabiliyordu ama hiçbir zaman bir girdi dosyası adına /Extensions bildirimi yazmaz; save kapısı böylece extension seviyesi 0'da PDF 1.7 görüyor ve 1.7 ExtensionLevel 3 bildiriyordu. Uydurma bir extension bildirimi yazmayı reddetmek bilinçlidir: kütüphane, yanlış bir kuralın üstünü örtmek için bir girdi dosyasını sessizce yükseltmez
Spesifikasyon doğrusal vakada açık ve nettir. Measure sözlükleri PDF 1.6'da geldi ve ISO 32000-1 §12.9 /Subtypea varsayılan RL verir; kendi girdi setiyle tanımlanan doğrusal bir koordinat sistemi: ölçek oranı, X ve Y sayı biçimleri, mesafe ve alan. Jeouzaysal ölçüm, PDF 1.7 üzerindeki Adobe Extension Level 3'ün sonradan eklediği şeydir; /Subtype /GEO ile tanınır ve coğrafi nokta dizileri, koordinat sistemi sözlükleri ve gösterim birimleri taşır — Delphi'de GeoPDF viewport'larını, GPTS ve LPTS dizilerini okumak yazısında gezilen yapılar. İki sözlük de aynı /Measure anahtarına asılıdır, dolayısıyla anahtarda duran hiçbir kural ikisi için de doğru olamaz. Ayırt edici bilgi bir seviye aşağıda, ölçüm sözlüğünün kendisinde durur
Düzeltilmiş kural seti hâlâ neleri dayatır?
Düzeltme koşulsuz anahtar kuralını siler ve gerçek sürüm gereksinimlerini anlatan kapıları bırakır. /VP ya da /UserUnit taşıyan bir sayfa CB_PagePDF16Entries üzerinden PDF 1.6 istemeye devam eder, bir /PtData anahtarı extension seviyesi 3 ister ve CB_GeospatialDictionary, bir ölçüm sözlüğünün jeouzaysal olup olmadığına ona ulaşan anahtara değil içeriğine bakarak karar verir
// Kaldırıldı: /Measure anahtarı taşıyan her sözlük jeouzaysal sayılıyordu
// AddExtensionRule(3, fmkDictKey, 'Measure', '/Measure geospatial dictionary', Nil);
AddRule(16, fmkCustom, '', 'Page PDF 1.6 entry /UserUnit /VP', CB_PagePDF16Entries);
AddExtensionRule(3, fmkDictKey, 'PtData', '/PtData geospatial dictionary', Nil);
AddExtensionRule(3, fmkCustom, '', 'geospatial measure dictionary', CB_GeospatialDictionary);
function CB_GeospatialDictionary(Obj: TPDFObject; const Ctx: TPDFRuleContext): Boolean;
var
Dict: TPDFDictionary;
begin
Result := False;
if not (Obj is TPDFDictionary) then
Exit;
Dict := TPDFDictionary(Obj);
Result := (Dict.StringValue('Subtype') = 'GEO') or
(Dict.FindIndexByKeyName('GCS') >= 0) or (Dict.FindIndexByKeyName('DCS') >= 0) or
(Dict.FindIndexByKeyName('GPTS') >= 0) or (Dict.FindIndexByKeyName('LPTS') >= 0) or
(Dict.FindIndexByKeyName('PDU') >= 0);
end;
Paylaşılan Delphi ve FPC regresyonları o sınırı iki taraftan sabitler. Ölçüm sözlüğü /Subtypeı atlayan bir viewport ile /RL açıkça yazan bir viewport PDF 1.6'da geçer, aynı sayfa PDF 1.5'te yine reddedilir ve özellik tespiti artık onun için extension bildirmez. Bir /GPTS dizisi eklemek kararı 1.7 ExtensionLevel 3e geri çevirir; extension seviyesi bildirildiğinde geçer ve çıplak bir /Subtype /GEO sözlüğü olmadan reddedilir. Callback tasarımdan muhafazakârdır: başıboş bir /GCS ya da /PDU anahtarı da taşıyan doğrusal bir sözlük jeouzaysal sayılır, çünkü o anahtarlar RL modelinde anlam taşımaz
LockSaveVersion, bu değişikliğin çağıranlara göründüğü yerdir. TPDFlib.LockSaveVersion '1.0'dan '1.7'ye kadar kabul eder, başka her şey için 0 döndürür, belge sürümünü sabitler ve yazıcı tarafı çağrıların onu sessizce yükseltmesini durdurur; save kapısı yine de kilitli değere göre çalışır. Düzeltilmiş kurallarla 1.6'da kilitli bir CAD dosyası temiz kaydolur. 1.6'da kilitli gerçek bir GeoPDF yine 602 alır; doğru cevap budur ve SetMeasureDictCoordinateSystem gibi jeouzaysal yazım çağrıları, o içeriği API üzerinden kurduğunuzda extension seviyesi 3'ü kendileri bildirir
if Pdf.LockSaveVersion('1.6') <> 1 then
raise Exception.Create('unsupported version string');
if Pdf.SaveToFile('floor-plan-16.pdf') <> 1 then
begin
if Pdf.LastErrorCode = PDFLIB_ERROR_VERSION_COMPLIANCE then
// 1.6 üzeri gerçek içerik, örneğin bir GEO ölçüm sözlüğü
raise Exception.CreateFmt('Locked at 1.6 but %s needs %s',
[string(Pdf.GetInformation(312)), string(Pdf.GetInformation(311))]);
end;
Pdf.UnlockSaveVersion;
Sürüm kuralı taraması neden gerektiğinden yavaştı?
Tarama, her TPDFFeatureRulei test etmeden önce yerel bir kayda kopyalıyordu; kayıt iki AnsiString alanı tuttuğu için her kopya iki referans sayısını düzeltiyor ve önceki değerleri serbest bırakıyordu. Preflight her nesne ağacının her düğümünü ziyaret eder, skalerler dahil; o maliyet nesne sayısı ile kural sayısını çarpıyordu ve hedef sürüme hiç uygulanmayan kurallar önce kopyalanıp sonra atlanıyordu. PDFFeatureRules birim başlangıcında bir kez doldurulduğu ve salt-okunur işlendiği için v3.539.17, tablo girdilerini doğrudan MatchSingleRule ile RuleExceedsTarget'e geçirir; ikisinin const Rule parametreleri dizelere dokunmadan referans alır
// Önce: kural başına, ziyaret edilen nesne başına yönetilen kayıt kopyası
Rule := PDFFeatureRules[X];
if not RuleExceedsTarget(Rule, TargetVersion, TargetExtensionLevel) then
Continue;
// Sonra: const parametreler değişmez tablo girdisini yerinde okur
if not RuleExceedsTarget(PDFFeatureRules[X], TargetVersion, TargetExtensionLevel) then
Continue;
if MatchSingleRule(Obj, Ctx, PDFFeatureRules[X]) then
begin
RequiredVersion := RequiredVersionString(PDFFeatureRules[X]);
FeatureName := PDFFeatureRules[X].Feature;
Result := False;
Exit;
end;
Ölçülen etki dardır ve öyle alıntılanmalıdır. Benchmark, 20.000 sayısal nesnelik bir diziyi bir PDF 1.4 hedefine karşı tur başına on kez sınar; -O2 ile FPC Win64'te derlenmiş hâliyle beş turun medyanı 0,711 s'den 0,203 s'ye indi ve iki derlemeyi ters sırada çalıştırmak 0,459 s karşılığında 0,150 s verdi. Bu, kural eşleştirme yolunda tek başına kabaca 3 katlık bir kazançtır. Gerçek bir save ayrıca ertelenmiş özellik tespiti, nesne çözümleme ve serileştirme öder, oran toplam save süresine taşınmaz. Kural sırası, callback'ler, sürüm eşikleri ve ilk başarısızlık tanısı değişmedi; oraya varmak için hiçbir kural save'ler arasında önbelleğe alınmadı ya da atlanmadı
Yüklü bir PDF sürüm preflight'ını geçemediğinde neye bakmalısınız?
Sürüme dokunmadan önce 311 ve 312 anahtarlarını okuyun. Özellik bir jeouzaysal sözlüğü adlandırıyorsa ve dosya yalnızca doğrusal ölçümler çiziyorsa, bu bu yanlış pozitifti ve güncel bir derleme dosyayı değişmeden kaydeder. Özellik gerçekse ya extension'ı bildirin ya da içeriği gerçekten içeren bir sürüme kilitlenin; kapıyı susturmak için sürümü yükseltmek, teslim ettiğinizi sonraki tüketicilerin okuyup okuyamayacağı sorusunu gizler. Sınırlı ve kanıta dayalı kontrol ilkesi, CAD çizimlerinin bir sürüm numarası yerine uygunluk standardıyla karşılaştığı mühendislik belgeleri için PDF/E-1 author-mode preflightında da geçerlidir
Sürüm uygunluk kontrolleri, ölçüm ve jeouzaysal sözlükler ve save sürüm kilitleme, PDF Library for Delphi, Delphi, C++Builder ve Lazarus geliştiricileri için PDFlibPas araç setinin parçalarıdır