Teknik Makale

PDF Optional Content Katmanlarını PDFium ile Açıp Kapatmak

Bir çizim takımı ölçülerini kendi katmanında getirir ve onları gizlemenin bariz yolu, o grubun referansını varsayılan konfigürasyonun /ON dizisinden /OFF'a taşımaktır. Dosya kaydedilip yeniden açılır ve ölçü çizgileri hâlâ başlık bloğunun üzerinden basılıdır. Neden bir nesne ötededir, grup sözlüğünün içindedir: /Usage << /View << /ViewState /ON >> >> — renderer'ın konfigürasyon dizilerinden önce okuduğu açık bir durum

Bir katmanın durumu PDF'te birkaç yerde yaşayabilir ve /OCProperties düzenlemesi bunlardan yalnızca birine dokunur. Google'ın PDFium motoru optional content'i doğru render eder ama genel C API'sinde optional content gruplarını (OCG) listelemeye, bir konfigürasyon seçmeye ya da bir grubu açıp kapatmaya yarayan işlev yoktur. Katman süren Delphi kodu, nesneleri kendisi yeniden yazıp renderer'ın karar sırasını tam olarak yeniden oynamak zorundadır; yoksa katman listesi bir durum, sayfa başka bir durum gösterir

PDFium'un render ettiğini değiştirmek için /ON ve /OFF'u düzenlemek neden yetmez?

/ON ve /OFF dizileri beş adımın yalnızca ikincisidir ve sonraki iki adım onları ezebilir. ISO 32000-1 §8.11.4.3 optional content konfigürasyon sözlüğünü /BaseState, /ON, /OFF, /Locked, /RBGroups ve /AS ile tanımlar; §8.11.4.4 ise bir OCG'nin taşıyabileceği Usage sözlüğünü. Belirtimde Usage, bir grubun ne işe yaradığını anlatır ve /AS'deki otomatik-durum girdileri o anlatımı ON ya da OFF'a çevirir. PDFium daha kısa bir yol izler: bir amaç için render ederken o amaçtaki grubun kendi açık durumu kazanır. Birleştirildiğinde sıra şöyle görünür:

  • Konfigürasyonun /BaseState'inden başlayın; /ON ile /Unchanged görünür sayılır, yalnızca /OFF gizler
  • /ON dizisini, sonra /OFF dizisini uygulayın; ikisinde de listelenen bir grup gizlenmiş kalır
  • İstenen amaç için grubun açık Usage durumunu uygulayın — örneğin /Usage << /View << /ViewState /OFF >> >> — bu, yukarıdakilerin hepsini ezer
  • /Intent'inde ne /View ne de /All bulunan bir grubu görünür sayın; view-intent görünürlüğüne katılmaz
  • Seçili konfigürasyonun /AS dizisini en son işletin; eşleşen event için girdiler, listeledikleri grupların durumunu belirler
PDFium Component'ın Delphi'de her PDF optional content grubu için yeniden oynattığı beş adımlı görünürlük kararı: BaseState başlangıcı belirler, konfigürasyonun ON ve OFF dizileri sırayla uygulanır, açık bir Usage ViewState ya da PrintState girdisi ikisini de ezer, Intent katılımı olmama görünür sayılır ve AS dizisi en son çalışır
ON ve OFF dizilerini düzenlemek yetmez, çünkü PDFium BaseState'i, iki diziyi, grup Usage durumunu ve son olarak AS otomatik-durum kurallarını tek bir hükme katlar; InspectOptionalContent bu hükmü adım adım yeniden üretir

Üçüncü adım açılıştaki dosyayı açıklar: bazı exportlar, oluşturdukları her gruba /ViewState /ON yazar ve o andan sonra dizi düzenlemesinin hiçbir hali o katmanları gizleyemez. Yazdırma ve export aynı kuralı tek bir kıvırımla izler: renderer önce /PrintState'i ya da /ExportState'i okur ve özel girdi yoksa /ViewState'e döner. Tek başına bir /ViewState /ON bu yüzden katmanı kâğıtta da sabitler. Dördüncü adım daha sessizdir. /Intent /Design etiketli bir grup view-intent görünürlüğünün büsbütün dışındadır ve dizilerden bağımsız boyar; /Intent anahtarı hiç olmayan bir grup ise /View sayılır — belirtimin verdiği varsayılan budur

InspectOptionalContent ile katmanları ve render durumlarını listelemek

PDFium Component o sırayı TPdf.InspectOptionalContent içinde yeniden oynar. Metot bir TPdfOptionalContentInventory doldurur ve başarıda True döndürür; Usage parametresinin varsayılanı ocuView, ConfigurationIndex parametresinin varsayılanı -1'dir ve bu, 0 indeksini seçer. Konfigürasyon indeksi 0 varsayılan /D sözlüğüdür; /Configs girdileri 1 indeksinden itibaren dosya sırasıyla izler. Taramadan önce metot, PDFium'dan açık belgeyi belleğe kaydetmesini ister ve nesne akışlarını genişletir; böylece oturumda daha önce yapılmış düzenlemeler sonuca yansır. /OCProperties taşımayan bir dosya ya da mevcut aralığın dışında bir konfigürasyon indeksi, çağrıyı ErrorMessage'de gerekçeyle False döndürür; iki durumda da özel durum fırlatmaz

procedure TFormMain.ListLayers;
var
  Inv: TPdfOptionalContentInventory;
  Cfg: TPdfOptionalContentConfiguration;
  G: TPdfOptionalContentGroup;
begin
  // Usage varsayılanı ocuView'dır; -1 konfigürasyon 0'ı, yani /D sözlüğünü seçer
  if not Pdf.InspectOptionalContent(Inv) then
  begin
    Memo1.Lines.Add('No usable layers: ' + Inv.ErrorMessage);
    Exit;
  end;
  Cfg := Inv.Configurations[Inv.SelectedConfigurationIndex];
  Memo1.Lines.Add(Format('Configuration %d "%s", %d groups, /AS present: %s',
    [Inv.SelectedConfigurationIndex, string(Cfg.Name), Length(Inv.Groups),
     BoolToStr(Cfg.HasAutomaticState, True)]));
  for G in Inv.Groups do
    Memo1.Lines.Add(Format('obj %d  %-24s visible=%s  locked=%s  radio=%d',
      [G.ObjectNumber, string(G.Name),
       BoolToStr(G.EffectiveVisible, True),
       BoolToStr(G.Locked, True), G.RadioGroupIndex]));
end;

Her TPdfOptionalContentGroup nesne numarasını, adını, Intents'ini ve ViewState, PrintState, ExportState açık durumlarını taşır; her biri ocsUnspecified, ocsOn ya da ocsOff'tur. Bir durum ocsOn okurken grup /OFF dizisinde duruyorsa, açılış paragrafındaki tuzağı buldunuz demektir. Locked ve RadioGroupIndex seçili konfigürasyondan gelir ve hiçbir /RBGroups kümesine ait olmayan bir grup için RadioGroupIndex -1'dir

Usage sözlüğünün gerisi, dosyanın bildirdiği gibi raporlanır: Language ve LanguagePreferred, UserType ve UserNames, PageElementSubtype ve bir HasZoomRange bayrağının arkasında MinimumZoom ile MaximumZoom. EffectiveVisible bunların hiçbirine bağlı değildir; yakınlaştırmaya bağlı gösterim görüntüleyicinizin işidir. Her TPdfOptionalContentConfiguration adını, BaseState'ini, dizi içeriklerini ve RadioGroups'unu açar; üstüne /Order'u grup referanslarına düzleştirip iç içe etiket dizilerinin metin etiketlerini atlayan OrderGroupObjectNumbers'u verir

Yazdırma ve export ekrana katılmayabilir

Usage argümanı cevabı değiştirir ve amacı da budur. ocuPrint altında envanter, /ViewState'e geri dönüşlü /PrintState'i okur ve yalnızca /Event'i /Print olan /AS girdileri katılır. Yalnızca kâğıtta görünmek üzere kurulmuş bir filigran katmanı bu yüzden ocuView altında gizli, ocuPrint altında görünür raporlar. İki geçişi karşılaştırmak bu katmanları, çıktı kimseyi şaşırtmadan önce yakalar

procedure TFormMain.ReportPrintOnlyLayers;
var
  OnScreen, OnPaper: TPdfOptionalContentInventory;
  I: Integer;
begin
  if not Pdf.InspectOptionalContent(OnScreen, ocuView) or
     not Pdf.InspectOptionalContent(OnPaper, ocuPrint) then
    Exit;
  // İki geçiş de aynı /OCGs dizisini okur; grup sırası uyuşur
  for I := 0 to High(OnScreen.Groups) do
    if OnScreen.Groups[I].EffectiveVisible <> OnPaper.Groups[I].EffectiveVisible then
      Memo1.Lines.Add(Format('%s: screen=%s, print=%s',
        [string(OnScreen.Groups[I].Name),
         BoolToStr(OnScreen.Groups[I].EffectiveVisible, True),
         BoolToStr(OnPaper.Groups[I].EffectiveVisible, True)]));
end;

OCMD görünürlük ifadeleri nasıl değerlendirilir?

Bir optional content üyelik sözlüğü (OCMD, ISO 32000-1 §8.11.2.2), bir içerik parçasını bir /P politikası ya da grupları /And, /Or ve /Not ile birleştiren bir /VE görünürlük ifadesi üzerinden birkaç gruba bağlar. Belirtim, /P'ye /AnyOn varsayılanını verir, /VE mevcutsa /OCGs ve /P'ye üstünlük tanır ve grupsuz bir OCMD'nin görünürlük üzerinde etkisi olmadığını söyler

Envanterin Memberships dizisi bu kuralları, aynı geçişin grup başına sonuçlarına karşı uygular; böylece istediğiniz kullanım sona kadar taşınır. Her üyelik grup nesne numaralarını, Policy'yi (ocmpAnyOn, ocmpAllOn, ocmpAnyOff, ocmpAllOff ya da bu dördü dışındaki bir ad için ocmpUnknown), ham VisibilityExpression metnini ve kendi EffectiveVisible'ını bildirir. Grupsuz bir OCMD görünür sayılır. Bilinen bir OCG olmayan bir nesne numarasına referans da görünür sayılır; böylece tek bir başıboş referans çevresindeki her şeyi gizlemez. /VE değerlendirmesi 32 iç içe derinlikte durur ve daha derinleri gizli sayar; kendine referans veren bir ifade yığını taşıramaz. Tarama, /Type /OCMD ile dolaylı nesne olarak saklanan her OCMD'yi listeler; nesne akışları dahil

SaveAsOptionalContentConfigured ile yeni bir katman durumu yazmak

TPdf.SaveAsOptionalContentConfigured, her biri bir GroupObjectNumber ve bir Visible bayrağı tutan TPdfOptionalContentStateChange kayıtlarından oluşan bir dizi alır. Beş adımlı sıra altında seçili konfigürasyonun tam olarak o durumu ürettiği bir kopya yazar. Bahsetmediğiniz gruplar, envanterin onlar için hesapladığı görünürlüğü korur

procedure TFormMain.SaveWithoutDimensions(DimensionsObj, NotesObj: Integer;
  ForPrint: Boolean);
var
  Changes: TPdfOptionalContentStateChanges;
  Options: TPdfOptionalContentConfigureOptions;
  Report: TPdfOptionalContentConfigureReport;
begin
  SetLength(Changes, 2);
  Changes[0].GroupObjectNumber := DimensionsObj;
  Changes[0].Visible := False;
  Changes[1].GroupObjectNumber := NotesObj;
  Changes[1].Visible := True;

  Options := TPdfOptionalContentConfigureOptions.Default;
  if ForPrint then
  begin
    Options.Usage := ocuPrint;         // /Print << /PrintState ... >> yazar
    Options.ConfigurationIndex := 1;   // ilk /Configs girdisi; /D olduğu gibi kalır
  end;

  if not Pdf.SaveAsOptionalContentConfigured('C:\Out\Drawing-NoDims.pdf',
    Changes, Options, Report) then
    raise Exception.Create('Layer update rejected: ' + Report.ErrorMessage);

  Log(Format('%d of %d groups changed, %d Usage states rewritten, /AS removed: %s',
    [Report.ChangedGroupCount, Report.GroupCount,
     Report.UpdatedUsageStateCount,
     BoolToStr(Report.RemovedAutomaticState, True)]));
end;

Çıktı, PDFium'un kendi kaydettiği belgeyi bayt bayt bir önek olarak tutar ve yalnızca yeniden yazılan konfigürasyon sahibini, Usage sözlükleri taşıyan OCG nesnelerini, ardından yeni bir xref bölümü ve trailer'ı ekler. Herhangi bir şey hedefinize ulaşmadan önce sonuç, sıkı yükleme politikası altındaki ayrı bir TPdf'te yeniden açılır ve o yükleme başarılı olmazsa ya da çapraz referans tablosu doğrulanmazsa metot başarısız olur. Dosya aşırı yükü, hedefin yanına geçici bir dosyaya yazar ve hedefi yalnızca doğrulamadan sonra değiştirir; reddedilen bir güncelleme önceki çizimi olduğu gibi bırakır. PDFium Component'taki PDF ad ağacı ve numara ağacı düzenleyicisinin kullandığı doğrulanmış artımlı revizyon yaklaşımının aynısıdır. Bir yazdırma ön ayarını teyit etmek için çıktıyı yeniden yükleyip InspectOptionalContent(Inv, ocuPrint, 1) çağırın; gruplar geçirdiğiniz durumları bildirmeli

PDFium Component'taki SaveAsOptionalContentConfigured katmanları açılıp kapatılmış bir Delphi PDF'ini nasıl yazar: durum değişiklikleri ve seçenekler girer, seçili konfigürasyon eksiksiz ON ve OFF dizileriyle Usage durumlarıyla yeniden yazılır, doğrulanmış artımlı revizyon eklenir ve sıkı bir yeniden açma, hiçbir şey yazılmadan önce doğrulamak zorundadır
Konfigüre kaydetme, PDFium'un kendi yeniden kaydını bayt önek olarak tutar, yeniden yazılan konfigürasyon sahibini artı yeni bir xref bölümünü ekler ve hedefe dokunulmadan önce sonucu ayrı bir TPdf içinde yeniden açar

Konfigüre kaydetme neyi yapmayı reddediyor?

Konfigüre kaydetme, belgenin yasakladığı ya da güvenle taşıyamayacağı her değişikliği reddeder ve her ret, hedef yazılmadan önce gerçekleşir. Kontroller sabit bir sırada çalışır. İmzalı bir belge, başka hiçbir şey olmadan EPdfError fırlatır; AllowSignedDocument = True geçirmediyseniz. Şifreli bir belge sonraki reddedilendir; çünkü düz metin artımlı nesneler etkin güvenlik işleyicisini taşıyamaz. Sonra istenen her değişiklik sınanır: /OCGs içinde olmayan bir nesne numarası büsbütün başarısız olur ve konfigürasyonun /Locked dizisinde listelenen bir gruba yapılan değişiklik başarısız olur; mevcut değerini yeniden bildirmek geçer. Son olarak EnforceRadioGroups açıktırken, birden fazla görünür üyeyle kalacak bir /RBGroups kümesi reddedilir. İmza reddi dışındaki her ret, gerekçeyi Report.ErrorMessage'e koyup False döndürür

SaveAsOptionalContentConfigured'ın PDFium Component'ta konfigüre bir Delphi PDF'ini yazmadan önce uyguladığı ret kapıları: OCG'ler dışındaki bir nesne numarası başarısız olur, kilitli gruplar başarısız olur, birden fazla görünür üyeli RBGroups kümeleri reddedilir, şifreli belgeler düz metin artımlı nesneleri taşıyamaz ve imzalı dosyalar AllowSignedDocument ister
Her ret, hedefe dokunulmadan önce gerçekleşir; gerekçe Report.ErrorMessage'e, dosya imzalıysa EPdfError iletisine düşer

O kapılardan ikisi belirtimin kesinlikle istediğinden daha ileri gider. §8.11.4.3, /Locked'ı görüntüleyicinin kullanıcı arayüzü üzerinden yapılacak değişikliklere karşı bir koruma olarak anlatır ve radyo düğmesi gruplarını, biri açıldığında öteki üyeleri kapatmak olarak. PDFium Component ikisini programatik değişikliklere de uygular ve radyo gruplarında üye seçip kapatmak yerine reddeder. Karşılıklı dışlayan bir kümenin iki üyesi istenince kütüphane hangisini kastettiğinizi bilemez ve bir tahmin, isteğinizle çelişirdi. Ötekileri aynı Changes dizisinde kapatın, kaydetme geçer

İmzalı belge kapısı şunun için vardır: eklenen revizyon, PDFium'un tam yeniden kaydının üstüne oturur ve bu yeni bir bayt dizisidir; mevcut bir imzanın kapsadığı bayt aralığının sağ kaldığı varsayılamaz. AllowSignedDocument = True'yu yalnızca o imzanın kaybı kabul edilebilirken geçirin

Toplu işte üç yan etki önem taşır. Yeniden yazım, seçili konfigürasyonu /BaseState /ON'a normalize eder; yazarın /Unchanged ya da /OFF tabanı, aynı görünürlüğü üreten açık dizilere dönüşür. /AS'yi düşürmek, yalnızca kâğıtta beliren filigran katmanı gibi yalnızca-yazdırma hilelerini de kaldırır; o kuralları tutmak isterseniz DisableAutomaticState'i False yapın ve kendi eventlerinizde istediğiniz durumu ezebileceklerini kabul edin. Ve /ON ile /OFF dizileri, Options.Usage için hesaplanan durumlardan yazılır; dolayısıyla konfigürasyon 0'a kaydedilen bir yazdırma ön ayarı, Usage sözlüğü olmayan her grup için ekranın gösterdiklerini de değiştirir. Yazdırma ön ayarlarını bir /Configs girdisinde tutun, /D'yi ekrana bırakın. Uyum tarafında PDF/A-2 (ISO 19005-2 clause 6.9) ve PDF/UA (ISO 14289-1 clause 7.10), konfigürasyon sözlüklerinde /AS'yi yasaklar; varsayılan çıktı, PDFium Component ile PDF/A preflight doğrulamasının yoksa bildireceği bir bulguyu baştan temizler

Katman değişikliklerini bir Delphi görüntüleyici içinde uygulamak

Görüntüleyicide katman kontrolü, envanterden kurulan bir kontrol listesi, ardından belleğe kaydetme ve yeniden yüklemedir. Kontrol listesini Groups'tan doldurun, Locked bayrağı ayarlı girdileri devre dışı bırakın, aynı RadioGroupIndex'i paylaşan üyeleri karşılıklı dışlayan sayın ve uygulamada stream aşırı yükü üzerinden bir TMemoryStream'e yazın. TPdf.LoadDocument etkin olmayan bir bileşen bekler; önce onu kapatın. Tamponlanmış yükleme baytları kopyalar; stream'i hemen serbest bırakabilirsiniz

procedure TFormMain.ApplyLayerChecklist(
  const Changes: TPdfOptionalContentStateChanges);
var
  Report: TPdfOptionalContentConfigureReport;
  Output: TMemoryStream;
  KeepPage: Integer;
begin
  Output := TMemoryStream.Create;
  try
    try
      if not Pdf.SaveAsOptionalContentConfigured(Output, Changes,
        TPdfOptionalContentConfigureOptions.Default, Report) then
      begin
        ShowMessage('Layer update rejected: ' + Report.ErrorMessage);
        Exit;
      end;
    except
      on E: EPdfError do
      begin
        ShowMessage(E.Message);       // imzalı dosya: Output hâlâ boş
        Exit;
      end;
    end;
    KeepPage := Pdf.PageNumber;
    Pdf.Active := False;
    Pdf.LoadDocument(Output);         // Buffered varsayılanı True
    Pdf.PageNumber := KeepPage;
  finally
    Output.Free;
  end;
end;

Yeniden yüklemeden sonra PDFium yeni konfigürasyondan boyar; ekranla sonraki InspectOptionalContent çağrısı aynı şeyi söyler. TPdf ile TPdfView arasındaki bağlantı, Delphi'de PDFium VCL ile özellik zengini bir PDF görüntüleyici kurma makalesinde işlenir. Belgenin henüz katmanı yoksa ve eklemeniz gerekiyorsa, Delphi'de OCG ve OCMD ile PDF katmanları oluşturma makalesi SaveAsOptionalContentCreated'i anlatır

Fiyatlandırma, deneme sürümü ve bileşenin diğer belge özellikleri PDFium Component for Delphi ürün sayfasında listelenir