Teknik Makale

PDF Library for Delphi: tagged PDF accessibility structure in Delphi

Erişilebilir bir PDF, görünen sayfanın asla göstermediği tek bir yapı üzerinde durur: ISO 32000-1 §14.7'de tanımlanan yapı ağacı. Bu, boyalı içeriğin üzerine katmanlanmış ve bir rol haritası aracılığıyla standart rollere eşlenmiş, başlıkların, paragrafların, tabloların ve şekillerin mantıksal bir hiyerarşisidir. Bir ekran okuyucu sayfadaki işaretleri değil, o ağacı okur. Onsuz, kusursuz görünen üretilmiş bir fatura anlamsal olarak boştur, çünkü içerik akışı çizim sırasını kaydeder, başka hiçbir şeyi değil. Toplam, kalem kalemlerden önce anons edilebilir, alt bilgi bir paragrafın içine kesebilir, kalemler tablosu tek bir farklılaşmamış sözcük dizisine çökebilir. Bunu önlemenin maliyeti sizin lehinize dengesizdir. Çizerken yapı yaymak dakikalar süren koddur; onu tamamlanmış belgelere sonradan eklemek bir iyileştirme projesidir. losLab PDF Library (PDF Library for Delphi), her çizim işlemini kendi mantıksal rolüne saran küçük bir çağrı kümesi aracılığıyla ağacı Delphi ve C++Builder'a açığa çıkarır

İşaretlenmiş içerik yapı ağacına nasıl bağlanır

İki katman işbirliği yapar. İçerik akışında, çizim işlemleri, her biri bir tam sayı MCID taşıyan işaretli içerik dizilerine parantezlenir. Belge kataloğunda, yapı ağacı bu MCID'leri, alternatif metin ve dil gibi özniteliklere sahip tipli öğelerin (H1, P, Table, Figure) bir hiyerarşisine eşler. Özel öğe türleri geçerlidir, ancak her biri rol haritası aracılığıyla standart bir role çözülmelidir (ISO 32000-1 §14.8.4). Çizgiler, arka planlar ve tekrarlanan sayfa süslemeleri gibi hiç anlam taşımayan içerik, yardımcı teknolojinin cümlenin ortasında onu okumak yerine atlaması için bir yapıt olarak işaretlenir

PDF Library for Delphi her iki katmanı da tek bir parantez çifti arkasında sürdürür. BeginTag, bir yapı öğesi açar ve işaretli içerik dizisini başlatır, çizim çağrıları onun içine iner ve EndTag ikisini de kapatır. Elle yazılmış etiketlemeyi takılan muhasebe, MCID'ler ve üst ağaç ve sayfa referansları, sizin yanlış yapamayacağınız içeride gerçekleşir

PDF Library for Delphi şeması: tamsayı MCID'ler taşıyan işaretli içerik dizilerini role map üzerinden bir H1, P ve Figure yapı ağacına bağlar; yapay öğeler okuma sırasının dışında tutulur
Tamsayı MCID'ler işaretli içerik dizilerini türü belirli bir yapı ağacına bağlar; rol haritası özel rolleri çözer ve yapımlar (artifact) okuma sırasının dışında kalır

Herhangi bir etiket açılmadan önce iki belge düzeyi anahtar işi çerçeveler. SetMarkInfo, belgenin etiketli olduğunu beyan eden katalog bayrağını yazar ve IsTaggedPDF onu geri okur, ki bu, gelen bir dosyanın korunmaya değer bir yapısı olup olmadığına karar verirken ucuz ilk yoklamadır. Dilin iki giriş noktası vardır. SetDocumentLanguage, belge varsayılanını tek başına ayarlarken, SetPDFUAMode onu tam PDF/UA çıktısını etkinleştirmenin bir parçası olarak ayarlar. Bir dosya, PDF/UA uygunluğu iddia etmeden yararlı bir şekilde etiketlenebilir ve aşamalı bir yayılım genellikle tam olarak orada başlar

Sonradan değil, çizerken etiketlemek

İşe yarayan üretim kalıbı, etiket parantezini sonraki bir geçiş olarak değil, her çizim çağrısının imzasının bir parçası olarak ele almaktır:

var
  Lib: TPDFlib;
begin
  Lib := TPDFlib.Create;
  try
    Lib.SetOrigin(1);                          // sol üst köşe orijini
    Lib.SetPDFUAMode('en-US');                 // kayıt sürümünü PDF 1.7'ye yükseltir
    Lib.SetInformation(1, 'Service Manual');   // PDF/UA için /Title zorunludur
    Lib.AddRoleMap('ManualTitle', 'H1');       // özel tür -> standart rol
    Lib.AddStandardFont(4);
    Lib.SetTextSize(18);
    Lib.BeginTagEx2('ManualTitle', '', '', 'en-US', '', 'h1-cover', '');
    Lib.DrawText(72, 96, 'Service Manual');
    Lib.EndTag;
    Lib.BeginTag('Figure', 'Exploded view of the gearbox assembly', '');
    Lib.AddImageFromFile('gearbox.png', 0);
    Lib.EndTag;
    Lib.BeginArtifact('Layout');               // sayfa süslemesi: okumadan hariç tutulur
    // ... çizgileri ve arka plan tonunu çiz ...
    Lib.EndArtifact;
    Lib.SaveToFile('manual.pdf');
  finally
    Lib.Free;
  end;
end;

Bu sıradaki üç çağrı uyumluluk ağırlığı taşır. SetPDFUAMode, PDF/UA çıktısını etkinleştirir ve belge sürümünü sessizce PDF 1.7'ye yükseltir, ki bu sürüm sabitlemeyle çatışır. LockSaveVersion ile PDF 1.4'e kilitlenmiş bir belge, UA modu etkinleştiğinde kaydetmeyi reddeder ve 602 hata kodu döndürür, bu, arşiv profilleri ve erişilebilirlik gereksinimleri farklı ekipler tarafından yapılandırıldığında ortaya çıkma eğiliminde olan bir çatışmadır. SetInformation(1, ...), ISO 14289'un görüntüleyicilerin dosya adı yerine göstermesini beklediği belge başlığını yazar; yokluğu, alanda en yaygın PDF/UA bulgularından biridir. AddRoleMap, özel ManualTitle türünü bir H1 olarak kaydeder ve bunu atlamak, aşağıda açıklanan tanıların eşlenmemiş bir rolü işaretlemesine yol açar

Başlık seviyeleri, bir sayfanın görünümü için yapılan geçici seçimler değil, kasıtlı bir politikayı hak eder. Ekran okuyucu kullanıcıları bölümler arasında başlık kısayoluyla atlar, bu yüzden görsel tasarımda ara seviye çok büyük göründüğü için H1'den H3'e giden bir şablon, o gezinimi sessizce bozar ve hiçbir görsel inceleme bunu asla yakalamaz. Bu tam olarak HEADING-LEVEL-SKIP tanısının adlandırmak için var olduğu kusurdur. Her şablonun görsel stillerini bir kez, tek bir yerde, sabit bir başlık merdivenine eşleyin, sapma hiç başlamaz

Bir ekran okuyucunun gerçekten gezinebileceği tablolar

Çizilmiş ızgara çizgileri ekran dışında hiçbir şey ifade etmez. Ekran okuyucuların gezindiği şey yapısal ilişkilerdir: hangi hücrelerin başlık olduğu, her başlığın neyi yönettiği ve düzensiz düzenlerde veri hücrelerinin başlıklara nasıl bağlandığı. Yapı öğesi özniteliği çağrıları üçünü de ele alır:

Lib.BeginTag('Table', '', '');
Lib.BeginTag('TR', '', '');
Lib.BeginTagEx2('TH', '', '', '', '', 'col-part', '');
Lib.SetStructElemScope('Column');          // yalnızca bu TH açıkken geçerlidir
Lib.DrawText(72, 120, 'Part');
Lib.EndTag;
Lib.BeginTagEx2('TH', '', '', '', '', 'col-torque', '');
Lib.SetStructElemScope('Column');
Lib.SetStructElemColSpan(2);               // başlık değer ve birim sütunlarını kapsar
Lib.DrawText(200, 120, 'Tightening torque');
Lib.EndTag;
Lib.EndTag;
Lib.BeginTag('TR', '', '');
Lib.BeginTag('TD', '', '');
Lib.SetStructElemHeaders('col-part');      // düzensiz tablolar için açık bağlama
Lib.DrawText(72, 140, 'M8 flange bolt');
Lib.EndTag;
Lib.EndTag;
Lib.EndTag; // Table

Sıralama kuralı katıdır ve sessizce uygulanır. Her SetStructElem* çağrısı, BeginTag'i ile EndTag'i arasında, o anda açık olan etikete uygulanır ve hiçbir etiket açık değilken ya da öznitelik geçerli olana uygulanmazken hiçbir şey fırlatmadan 0 döndürür. Yanlış yerleştirilmiş bir çağrı basitçe yok olur. Geliştirme sırasında dönüş değerlerini iddialara sarmak, hâlâ görebiliyorken sapmayı yakalar; kendi haline bırakılırsa, eksik bir kapsam yalnızca bir erişilebilirlik denetimi tabloda gerçek bir ekran okuyucu çalıştırdığında ortaya çıkar. BeginTagEx2 aracılığıyla geçirilen öğe ID'leri ID ağacını besler (ISO 32000-1 §14.7.4) ve SetStructElemHeaders bağlamasını her şeyden önce çözülebilir yapan da budur

Aynı öznitelik ailesi, yardımcı teknolojinin dayandığı geri kalanı da kapsar. SetStructElemListNumbering, liste öğelerinin nasıl etiketlendiğini beyan eder, bu yüzden bir ekran okuyucu madde işareti gliflerini okumak yerine liste içindeki konumu anons eder. SetStructElemBBox, yeniden akış görünümlerinin içeriği yerleştirmek için kullandığı şekillerin ve tabloların sınırlayıcı kutusunu kaydeder. SetStructElemActualText, gliflerini okunabilir karakterlere eşlemeyen parçalar için, vektör sanattan oluşturulmuş bir büyük baş harf gibi, yerine geçecek metin sağlar. Her biri aynı kuralı izler: açık etikete bağlanır, ya da yok olur

PDF Library for Delphi tablo şeması: TH kapsamını, ikilik colspan'ı ve veri hücrelerine bağlanan bir headers özniteliğini, öznitelik çağrılarının yalnızca etiketleri açıkken bağlandığı kuralının yanında gösterir
Ekran okuyucular çizili çizgiler yerine TH scope, colspan ve headers bağlantılarını izler; öznitelik çağrıları ise yalnızca etiketleri açıkken bağlanır

Yapıtlar, dil ve kaydetme öncesi tanı kapısı

Tekrarlanan sayfa süslemesi, yani çalışan başlıklar, katlama işaretleri, filigranlar ve arka plan tonları, hiçbir zaman okuma akışına girmemesi için BeginArtifact ve EndArtifact parantezlerinin içine ait olmalıdır. Dil miras alınabilirdir. Belge varsayılanı SetPDFUAMode argümanından gelir ve başka bir dildeki bir parça, BeginTagEx veya SetStructElemLang aracılığıyla öğe başına onu geçersiz kılar. İngilizce bir kılavuzun içindeki bir Fransızca alıntıyı telaffuz edilebilir tutan şey budur

Kaydetmeden önce, GetPDFUADiagnostics, kütüphanenin yapısal kontrollerini bellek içi belge üzerinde çalıştırır ve bulguları metin olarak döndürür, boş bir dize hiçbir şey bulunmadığı anlamına gelir. Kodlar, klasik yazarlık hatalarını doğrudan adlandırır: alternatif metni olmayan bir görüntü için FIGURE-NO-ALT, bir H1'i izleyen bir H3 için HEADING-LEVEL-SKIP, hiç kaydedilmemiş özel bir tür için ROLEMAP-UNMAPPED. Bunu derlemeye bağlayın (belge kümesini üretin, boş olmayan tanılarda adımı başarısız kılın) ve erişilebilirlik regresyonları, aylar sonraki denetim bulguları yerine derleme zamanı tarzı hatalar haline gelir. Tam uyumluluk kararı, bazı normalleştirmeler yalnızca serileştirme sırasında uygulandığından, hâlâ kaydedilmiş dosya üzerindeki preflight'a aittir, Delphi'de PDF/A ve PDF/UA ön kontrolü makalesinde ele alınmıştır

Açıklama gezinimi kendi düğmesine sahiptir. PDF/UA, form alanlarının ve bağlantıların klavye ile gezinmesinin yapı sırasını izlemesini bekler ve SetTabOrderMode, görüntüleyicilerin uyduğu sayfa düzeyi tab sırası girdisini yazar, gelen dosyaları denetlemek için GetTabOrderMode kullanılabilir. Bu, yalnızca klavye kullanan bir kullanıcı hatayı bildirene kadar kimsenin fark etmediği türden bir gereksinimdir ve doğru yapmak belge başına bir çağrıya mal olur

Yapı ağaçları her birleştirmeden sağ çıkmaz

Etiketlenmiş belgeler, yalnızca sonraki her işleme adımı ağacı korursa etiketli kalır ve PDF Library for Delphi içindeki keskin kenar, merge-list ailesidir. MergeFileListFast, yapı ağacı korumasını hız için takas eder. Bu, taranmış görüntü toplu işleri için doğru, etiketli raporlar için yanlış bir takastır, çünkü çıktı iyi açılır, aynı şekilde render edilir ve sessizce erişilebilirlik katmanını kaybetmiştir. Herhangi bir girdi etiketliyse varsayılan MergeFileList'i ya da katı varyantı kullanın ve IsTaggedPDF'yi montaj sonrası iddiaların bir parçası yapın, böylece düzleştirilmiş bir toplu iş kimse fark etmeden gönderilemez. Büyük belge kümeleri için montaj hatları bu türden daha fazla takas taşır, büyük PDF birleştirme, bölme ve doğrudan erişim makalesinde incelenmiştir

PDF Library for Delphi şeması: GetPDFUADiagnostics, boş bir dize veya preflight kaydedilen dosyayı yargılamadan önce derlemeyi düşüren FIGURE-NO-ALT gibi adlandırılmış bulgular döndürür
GetPDFUADiagnostics, FIGURE-NO-ALT gibi bulguları kaydetmeden önce bildirir; derlemeye bağlanan boş olmayan bir sonuç, adımı anında düşürür

Doğrulama döngüsü kütüphanenin dışında kapanır: çıktıyı Acrobat'ta açın, etiketler panelini inceleyin ve her şablon ailesinden en az bir belgeyi gerçek bir ekran okuyucuyla okuyun. Tanılar yapısal hataları yakalar; yalnızca insan kulağı, teknik olarak geçerli ama pratikte kafa karıştırıcı bir okuma sırasını yakalar. Değerlendirme sürümleri ve eksiksiz etiketleme API referansı losLab PDF Library for Delphi ürün sayfasındadır