Teknik Makale

Delphi'de Taranan Görüntüleri Tek PDF'de Birleştirme

Yaprak beslemeli bir tarayıcı turu, size sayfa başına bir JPEG'den oluşan bir klasör bırakır: 0001.jpg, 0002.jpg ve devamı. Kimse bir vaka dosyasını yüzlerce küçük resme tıklayarak incelemek istemez; arşivin ihtiyacı olan şey, sayfaları sırayla duran, vaka dosyası başına tek bir PDF'dir. Numaralandırılmış bir tarama yığınını tek bir sıralı PDF'ye dönüştüren son adım da asıl buradaki iştir

PDFium Bileşeni (Component) bunu doğrudan (directly) halleder. Oluşturma (rendering) ve metin çıkarmanın (extraction) ötesinde, bileşen sıfırdan bir PDF oluşturabilir: boş bir belge oluşturun, istediğiniz boyutta (sized however you like) boş bir sayfa ekleyin, kullanıcı uzayı (user-space) koordinatlarında o sayfaya bir görüntü bırakın (drop), ardından kaydedin. Tüm boru hattı (pipeline) TPdf bileşeni üzerinde yaşar, bu nedenle bir toplu (batch) dönüştürücü dosya adları üzerinde bir döngü artı (plus) bir avuç çağrıdır (calls)

Bir Delphi toplu iş hattı, numaralı taramalardan oluşan bir klasörü tek sıralı bir PDF'ye çevirmek için PDFium Component AddPage, PageNumber, AddImage ve SaveAs çağrılarını kullanır
Her tarama, AddPage'in kurduğu ve AddImage çizmeden önce PageNumber'ın hedeflediği bir sayfa olur; SaveAs biten belgeyi bir kez yazar

Dönüşümün (conversion) şekli

Her tarama (scan) için üç şeyin gerçekleşmesi gerekir. Sayfa boyutuna (size) karar (decide) verirsiniz, bir kenar boşluğu (margin) bırakarak görüntüyü (image) sayfanın içine (inside) yerleştirirsiniz ve bir sonraki sayfaya (page) ilerlersiniz (advance). PDFium Bileşeni (Component) her biri (each) için size bir yöntem (method) sunar (gives): AddPage verilen (given) bir boyutta boş (blank) bir sayfa oluşturur (creates), AddImage (veya zaten (already) bir TPicture tutuyorsanız (hold) AddPicture) bit eşlemi (bitmap) geçerli (current) sayfaya (page) çizer (draws) ve PageNumber bileşene daha (subsequent) sonraki çizim çağrılarının (calls) hangi (which) sayfayı hedeflediğini (target) söyler

İnsanları çelmeleyen (trips up) tek (one) detay koordinat (coordinate) sistemidir (system). PDF kullanıcı (user) alanı (space), Delphi geliştiricilerinin refleks olarak uzandığı (reach for) ekran (screen) koordinatlarının aksine (opposite), orijini (origin) sayfanın sol (lower-left) alt (lower-left) köşesine (corner) koyar ve Y yukarı doğru (upward) artar (increasing). AddImage'a geçirdiğiniz (pass) X, Y görüntü dikdörtgeninin (rectangle) sol (lower-left) alt köşesidir ve Width, Height kaynak dosyanın (file) piksel boyutu değil, nokta (points) cinsinden (in) yerleşim (placement) boyutudur. Bunu geriye (backwards) alın ve taramalarınız sayfanın (page) dışına iner (land) veya (or) onları beklediğiniz yere (where) göre ters (upside down) döner

Belgeyi (document) ve (and) tarama (scan) başına bir (a) sayfa oluşturma

Boş (empty) bir belge (document) ile (with) başlayın (Start). CreateDocument taze (fresh) bir PDF ayırır (allocates) ve (and) bileşeni (component) aktif (active) bırakır (leaves), bu nedenle ayrı (separate) bir (no) açık (open) adım (step) yoktur (no). Oradan taranmış dosyaların listesinde yürürsünüz (walk) ve her biri için (for each one) bir sayfa ekler (add), onu geçerli (current) yapar (make) ve görüntüyü yerleştirirsiniz (place). Buradaki sayfa (Page) boyutları (dimensions), arşivlenmiş (archived) yazışmalar (correspondence) için standart sayfa (sheet) boyutu (size) olan nokta (points) cinsinden (in) A4'tür (595 × 842 dikey (portrait))

procedure TArchiveForm.ScansToPdf(const Files: TStrings; const OutputPath: string);
const
  PageW = 595.0;   // nokta cinsinden A4 genişliği
  PageH = 842.0;   // nokta cinsinden A4 yüksekliği
  Margin = 36.0;   // her tarama çevresinde yarım inçlik kenar boşluğu
var
  I: Integer;
  Pdf: TPdf;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.CreateDocument;                       // new, empty, already active
    for I := 0 to Files.Count - 1 do
    begin
      Pdf.AddPage(I + 1, PageW, PageH);       // 1 tabanlı sayfa dizini
      Pdf.PageNumber := I + 1;                // yeni sayfayı geçerli yap
      PlaceScan(Pdf, Files[I], PageW, PageH, Margin);
    end;
    Pdf.SaveAs(OutputPath);
  finally
    Pdf.Free;
  end;
end;

Her yineleme (iteration) bir sayfa (page) oluşturur (creates) ve (and) hemen (immediately) ona PageNumber'ı ayarlar (sets). Bu (That) ikinci (second) satır (line) önemlidir (matters): AddPage sayfayı ekler (inserts) ancak (but) çizim (draw) yöntemleri (methods) hangi (whichever) sayfa (page) geçerliyse (current) onda (on) hareket eder (act), bu nedenle (so) PageNumber'ı ayarlamak (setting), AddImage'ı yeni (just) yaptığınız sayfaya yönelten (aims) şeydir (what). Onu atlayın (Skip it) ve resimleriniz daha (before) önce (before) yüklü (loaded) olan hangi sayfa (page) tesadüfen (happened to) geldiyse (happened to) onun üzerine istiflenir (stack)

Bu döngüde bir varsayım (assumption) saklanıyor (hides): Files sırası (order). Bir tarayıcı (scanner) sayfaları (pages) 0001.jpg'den 0100.jpg'ye kadar (through) isimlendirir (names), ancak (but) bir dizin (directory) numaralandırması (enumeration) bunları her zaman sıralanmış (sorted) olarak (always) döndürmez (does not) ve (and) page10.jpg'nin (page10.jpg) yanındaki (next to) page9.jpg'ye bastığınız an (moment) düz (plain) bir dize (string) sıralaması (sort) sayfa 10'u sayfa 9'un önüne koyar (puts). Döngüden (loop) önce listeyi (list) açıkça (explicitly) sıralayın (Sort) ve (and) tarama (scan) zamanında (time) sözlüksel (lexical) sıranın (order) sayfa (page) sırasıyla eşleşmesi (matches) için sıfırla desteklenmiş (zero-padded) isimleri (names) tercih (prefer) edin. Sayfa (Page) dizisi (sequence), bir inceleyicinin (reviewer) anında (immediately) fark ettiği (notices) tek (one) şeydir (thing) ve (and) önlenmesi (prevent) en (cheapest) ucuz hatadır (mistake)

Bir taramayı (scan) yerleştirmek ve en (aspect) boy oranını korumak (keeping)

Bir tarama (scan) nadiren sayfa (page) ile aynı (same) şekildedir (shape). Yaprağı (sheet) doldurmak (fill) için gererseniz (stretch) metni (text) bozarsınız (distort); tam piksel (pixel) boyutunda (size) yerleştirirseniz (place) taşar (overflows). Çözüm (fix), iki orandan (ratios) daha (smaller) küçük olanına, genişliğe (width-fit) sığdırma (width-fit) veya yüksekliğe (height-fit) sığdırma (height-fit), göre ölçeklendirmek (scale) ve (and) geriye kalanı (left over) ortalamaktır (center). Orijin (origin) sol altta (lower-left) oturduğundan (sits), ortalamak (centering), kalan boşluğu (space) eşit olarak (evenly) bölmek (splitting) ve hem X hem de Y'ye eklemek (adding) anlamına gelir (means)

Bir A4 sayfa şeması, PDFium Component AddImage'nin Delphi kodu ve sol-alt başlangıç noktası kullanarak ölçeklenmiş bir taramayı kenar boşluklarının içine nasıl yerleştirdiğini gösterir
AddImage yerleştirme dikdörtgeninin sol alt köşesini alır; böylece sığdırma ve ortalama, başlangıç noktasından sayfa punto cinsinden hesaplanır
procedure TArchiveForm.PlaceScan(Pdf: TPdf; const FileName: string;
  PageW, PageH, Margin: Double);
var
  Pic: TPicture;
  AvailW, AvailH, Scale, DrawW, DrawH, X, Y: Double;
begin
  Pic := TPicture.Create;
  try
    Pic.LoadFromFile(FileName);              // VCL grafik birimleri aracılığıyla BMP, JPG, PNG vb.

    AvailW := PageW - 2 * Margin;
    AvailH := PageH - 2 * Margin;

    // Taramanın biçimini bozmadan kenar boşluklarının içine sığdırın.
    Scale := Min(AvailW / Pic.Width, AvailH / Pic.Height);
    DrawW := Pic.Width * Scale;
    DrawH := Pic.Height * Scale;

    // Ortala: kalan alan eşit şekilde bölünür. Y, sayfanın altından ölçülür.
    X := (PageW - DrawW) / 2;
    Y := (PageH - DrawH) / 2;

    Pdf.AddImage(FileName, X, Y, DrawW, DrawH);
  finally
    Pic.Free;
  end;
end;

Bu, piksel (pixel) boyutlarını (dimensions) okumak için dosyayı bir kez yükler, tek bir (single) tek tip (uniform) ölçek hesaplar ve yerleşim (placement) dikdörtgenini (rectangle) AddImage'a iletir (passes). AddImage doğrudan (directly) bir dosya yolu (path) kabul eder (accepts) ve onu AddPicture ile aynı görüntü ardışık düzeninden (pipeline) yönlendirir (routes), böylece (so) VCL grafik (graphics) birimlerinin (units) tanıdığı herhangi bir (any) format (format), özel durum (special-casing) gerektirmeden çalışır (works). Resmi önizleme bölmesinden (preview pane) bir TPicture'da zaten deşifre ettiyseniz (decoded), aynı (same) dikdörtgen (rectangle) ile AddPicture(Pic, X, Y, DrawW, DrawH)'yi çağırın ve ikinci dosya okumayı atlayın (skip)

JPEG taramaları (scans) için deşifrelemeyi (decode) atlama (Skipping)

Tarayıcılar neredeyse her zaman JPEG üretir. Bir JPEG'i TPicture'a yüklemek, onu tam bir bitmap'e çözer ve PDF'e giren şey de bu bitmap'tir. Çözme işlemi kaliteden ödün vermez ve PDFium pikselleri kayıpsız saklar, bu yüzden sayfa aynı görünür; ödediğiniz şey boyut ve zamandır, çünkü kompakt bir JPEG olarak gelen tarama, kayıpsız sıkıştırılmış ham pikseller olarak yazılır ve genellikle geldiği dosyadan çok daha büyük olur. AddJpegImage, orijinal sıkıştırılmış baytları bir akıştan doğrudan sayfaya gömer; böylece çıktı taramaların boyutuna yakın kalır ve toplu iş, çözme işini tamamen atlar

AddJpegImage özgün JPEG baytlarını bir PDFium Component sayfasına gömerken AddImage çözümler ve SaveAs, Delphi'de pikselleri yeniden kodlar
AddJpegImage tarayıcının sıkıştırılmış baytlarını olduğu gibi gömer; AddImage ile SaveAs'in yaptığı çözme ve yeniden kodlama geçişlerini önler
var
  Stream: TFileStream;
begin
  // ... geçerli sayfa için AddPage + PageNumber'dan sonra ...
  Stream := TFileStream.Create(FileName, fmOpenRead);
  try
    // JPEG baytlarını olduğu gibi gömer; kod çözme/yeniden kodlama döngüsü yoktur.
    Pdf.AddJpegImage(Stream, X, Y, DrawW, DrawH);
  finally
    Stream.Free;
  end;
end;

Piksel boyutlarına (dimensions) ölçeklemek (scale) için (since) hala X, Y, DrawW ve (and) DrawH'yi aynı (same) şekilde (way) hesaplıyorsunuz (compute). Onları (those) dosyadan (file) veya hızlı (quick) bir (a) başlık (header) ayrıştırmasından (parse) okuyun (Read), ardından (then) ham (raw) akışı (stream) AddJpegImage'a elden (hand) verin. PNG veya (or) TIFF taramaları (scans) için (For) AddImage yolu (path) doğru (right) olandır (one); JPEG kısayolunu (shortcut) gerçekten (actually) geçerli olduğu format (format) için ayırın (reserve)

Her (each) sayfayı (page) etiketleme (Labeling)

Her (each) sayfa (page) kaynak (source) dosya adını taşıdığında (carries) arşivlenmiş (Archived) taramaların denetlenmesi (audit) daha (easier) kolaydır. AddText kullanıcı alanı (user-space) koordinatında (coordinate) bir (a) dize (string) çizer (draws), böylece (so) bir altyazı (caption) görüntünün hemen (just) altında (under) oturur (sits). Ters çevrilmiş (inverted) Y eksenini unutmayın (Remember): taramanın (scan) altına (below) bir (a) etiket (label) koymak (put) için (to), ona (it) eklemek (add to) yerine (rather than) görüntünün alt (bottom) kenarından çıkarırsınız (subtract)

// Taramanın altındaki başlık: Y, sayfanın altına doğru azalır.
Pdf.AddText('File: ' + ExtractFileName(FileName), 'Helvetica', 9,
  X, Y - 14, clGray);

Kaydetme konusunda son bir nokta. SaveAs bir Boolean döndürür ve False, belgenin serileştirilemediği anlamına gelir; yazmanın başarılı olduğunu varsaymak yerine dönen değeri kontrol edin. Dosya sistemi sorunları başka bir yoldan gelir: eksik bir çıktı klasörü ya da kilitli bir hedef dosya, dosya katmanından bir istisna fırlatır; bu yüzden bir toplu iş, hem sonuç kontrolünü hem de hangi vaka dosyasının başarısız olduğunu günlüğe yazan bir işleyici ister. Döngü bitip dosya yazıldığında, elimizde tam olarak arşivin ihtiyaç duyduğu şey vardır: vaka dosyası başına bir sıralı PDF, sayfaları sığacak şekilde ölçeklenmiş, herhangi bir görüntüleyicide okumaya hazır

Aynı yapı taşları (building blocks) ilgili işleri kapsar. Sayfa (per-page) başına (per) boyutlandırma (sizing) kuralını değiştirin ve yaprak başına bir görüntü (image) içeren (with) bir fotoğraf kitabı elde edersiniz; döngüyü tutun (keep) ancak çok (multi-page) sayfalı (multi-page) bir TIFF kaynağından (source) okuyun (read) ve (and) bir faks arşivi (fax-archive) dönüştürücüsüne sahip olun. Programlı (programmatically) olarak PDF'ler (PDFs) oluşturmanın (building) daha (wider) geniş (wider) bir resmini istiyorsanız, PDFium Bileşeni (Component) ile sıfırdan PDF belgeleri oluşturma konusuna (see) bakın (see); sonucu daha (later) sonra (later) tekrar ekrana oluşturmak (render) için (to), PDFium Bileşeni ile PDF sayfalarını JPEG resimlerine dönüştürme konusuna bakın

loslab.com'dan PDFium Bileşeni, bu (this) seri (series) boyunca (throughout) kullanılan belge-oluşturma, oluşturma ve metin API'lerini (APIs) bir araya getirir (bundles)