Teknik Makale

PDFium İle Form XObjects Üzerinden Yeniden Kullanılabilir Sayfa Damgaları

Bir belgenin her sayfasına (every page) bir filigran (watermark) veya logo (logo) damgalamak (Stamping), sonucu bir dosya boyutu (file-size) denetleyicisinde (inspector) açana (open) kadar beş dakikalık (five-minute) bir iş (job) gibi görünür (looks like). Bariz (obvious) yaklaşım (approach), sayfaları (pages) dolaşmak (walk) ve her birinde aynı metin (text) veya görüntü (image) nesnelerini (objects) yeniden oluşturmaktır (build). Bu görsel olarak (visually) işe yarar (works) ve katlanarak artan bir şekilde israftır (wasteful). Yüz sayfalık (hundred-page) bir rapora (report) doğrudan (directly) çizilmiş diyagonal (diagonal) bir "DRAFT" (TASLAK) filigranı (watermark), içerik (content) akışlarında (streams) oturan (sitting) aynı yol (path) ve metin (text) verilerinin yüz kopyasıdır (hundred copies) ve kaydedilen (saved) dosya (file) bunların her birini (every one of them) taşır (carries)

Bir Form XObject, tam da (exactly) bundan (this) kaçınmak (avoid) için PDF'in sağladığı (provides) yapıdır (construct). Bir parça (piece) yeniden kullanılabilir içeriği (reusable content), bütün bir sayfayı (whole page) veya küçük bir şablonu (small template), birçok pozisyonda (many positions) birçok kez (many times) boyanabilen (painted) tek bir adlandırılmış (named) nesne (object) içine sarar (wraps). İçerik (content) dosyada (file) bir kez (once) yaşar (lives). Damgayı (stamp) isteyen (wants) her sayfa (Each page), "XObject N'i buraya (here), bu dönüşümle (transform) boya" diyen kısa (short) bir talimat (instruction) tutar (holds). Yüz sayfalık bir filigran, dosyaya yüz (hundred) tane (rather than) yerine bir içerik nesnesi ekler ve bu (that), sayfa sayısıyla (page count) doğrusal (linearly) olarak büyüyen (grows) bir belge ile büyümeyen (does not) belge arasındaki farktır (difference). Filigranlar (Watermarks), logo damgaları, sayfa numarası (page-number) şablonları ve mühürlerin (seals) hepsi (all) aynı problem (problem) şeklindedir (shape) ve Form XObject bunların her biri (every one of them) için doğru araçtır (tool)

Neden tek (one) bir depolanmış (stored) nesne, yüz yeniden çizimi (redraws) yener (beats)

Tasarruf (saving) yapısaldır (structural), kozmetik (cosmetic) değil. Bir PDF sayfası (page), çizim operatörleri (operators) dizisi (sequence) olan içerik akışını (content stream) yürüterek (executing) oluşturur (renders). Sayfa başına (per page) bir damgayı yeniden çizdiğinizde (redraw), o damga (stamp) için tam (full) operatör dizisini her sayfanın (every page) akışına (stream) eklersiniz (appending) ve baytlar (bytes), sahip olduğunuz (have) sayfa sayısı kadar (as many times as) çoğaltılır (duplicated). Bir Form XObject, bu (those) operatörleri belgede (document) bir kez saklanan (stored once) tek (one) bir akışa (stream) taşır (moves). Bireysel (individual) bir sayfanın tuttuğu (keeps) referans (reference) küçüktür (small): bir dönüşüm matrisi (transformation matrix) iter (pushes), XObject'i çağırır (invokes) ve durumu (state) geri yükler (restores). Sayfa sayısı artık (no longer) resmin (artwork) maliyetini (cost) çarpmaz (multiplies)

Bu (This) en çok damga ağır (heavy) olduğunda (when) önemlidir (matters). Yüzlerce yol parçası (path segments) içeren vektör bir mühür (vector seal) veya bir logo bit eşlemi (bitmap) saklanması pahalıdır (expensive). Bir kez saklandığında ve referans verildiğinde (referenced), ağır kısım (heavy part) tek bir kez (single time) ödenir ve sayfa başına genel yük (overhead), birkaç baytlık (a few bytes) çağrıdır (invocation). Sayfadaki (page) görsel sonuç (visual result), doğrudan yeniden çizimle (direct redraw) aynıdır (identical), ki asıl mesele (point) de budur (which is). Okuyucu (reader) farkı (difference) anlayamaz (cannot tell); dosya boyutu (file size) ise bunu gayet iyi (very much) anlar (can)

Bir sayfayı (page) XObject'e dönüştürmek (Capturing)

PDFium, yeniden kullanılabilir (reusable) nesneyi (object) mevcut (existing) bir sayfadan (page) oluşturur (builds). Kaynak (source), açık olan bir belgedeki bir sayfa, filigran sanat eserinizden başka bir şey içermeyen (contains nothing but) küçük tek sayfalık (one-page) bir PDF veya daha büyük (larger) bir dosyanın belirli (particular) bir sayfasıdır (page). CreateXObjectFromPage, o kaynak (source) sayfanın (page's) içeriğini, hedef (destination) belgeye (document) (damgaladığınız / stamping olana) ait (belongs to) yeniden kullanılabilir bir tanıtıcıya (handle) yakalar (captures)

var
  Dest, Stamp: TPdf;
  XObject: TPdfXObject;
begin
  Dest := TPdf.Create(nil);
  Stamp := TPdf.Create(nil);
  try
    Dest.FileName := 'Report.pdf';
    Dest.Active := True;
    Stamp.FileName := 'Watermark.pdf';   // one page of artwork
    Stamp.Active := True;
    if not (Dest.Active and Stamp.Active) then
      raise Exception.Create('Could not open the input documents');

    // Capture page 0 of the stamp document into a reusable handle that
    // is owned by Dest. Source must be Active; the index is zero-based.
    XObject := Dest.CreateXObjectFromPage(Stamp, 0);
    if XObject = nil then
      raise Exception.Create('Could not build the stamp XObject');
    // ... place it, then free it before closing Stamp (see below) ...

İmza (signature), CreateXObjectFromPage(Source: TPdf; SourcePageIndex: Integer): TPdfXObject şeklindedir (is). Yöntem (method), kaynak belge Active değilse yükseltir (raises) ve PDFium nesneyi oluşturamadığında (cannot build) yükseltmek (raising) yerine nil döndürür (returns), bu nedenle yukarıdaki açık kontrol (explicit check) isteğe bağlı (optional) değildir. Geri gelen tanıtıcı (handle), sahibi olduğunuz (you own) bir TPdfXObject nesnesidir ve ona (it) iliştirilmiş (attached) iki (two) yaşam süresi kısıtlaması (lifetime constraints), insanları yakalayan (catches people out) tüm bu (this whole) alıştırmanın (exercise) parçasıdır, bu nedenle aşağıda kendi (their own) bölümlerini alırlar (get)

Damgayı bir sayfaya yerleştirmek

Yakalanan (captured) bir XObject kendi başına (on its own) hiçbir şey (nothing) yapmaz. Görünmesini (appear) sağlamak (To make) için, 1 tabanlı (1-based) PageNumber özelliği tarafından (by) seçilen (selected) belgenin mevcut (current) sayfasına (page), InsertFormObjectFromXObject ile onun bir kopyasını (copy) eklersiniz (insert). Bu çağrı (call), altta yatan (underlying) sayfa nesnesini, bir FPDF_PAGEOBJECT döndürür (returns) ve döndürülen tanıtıcı (handle), yerleşimi (placement) nasıl konumlandıracağınızdır (position). Bir dönüşüm (transform) olmadan, damga kaynak sayfanın (source page's) kendi koordinatlarında (coordinates) orijine (origin) iner (lands), ki bu nadiren (rarely) istediğiniz yerdir (where you want it)

InsertFormObjectFromXObject çağrı başına (per call) bir (one) kopya (copy) eklediği (inserts) ve her seferinde (each time) yeni bir (fresh) sayfa nesnesini (page object) geri verdiği (hands back) için, aynı (same) XObject'i bir (one) sayfada farklı dönüşümlerde (different transforms) birkaç kez (several times) boyayabilirsiniz (paint) ve saklanan (stored) içerik (content) dosyada (file) hala (still) bir (once) kez (once) sayılır (counted). Bir köşe logosu (corner logo) ve soluk (faint) tam sayfalık (full-page) bir filigran (watermark), aynı (same) yakalanan (captured) nesneden gelebilir (can come)

var
  PageObj: FPDF_PAGEOBJECT;
  M: TPdfMatrix;
  RawM: FS_MATRIX;
begin
  // The current page of Dest receives one copy of the XObject.
  PageObj := Dest.InsertFormObjectFromXObject(XObject);
  if PageObj = nil then
    raise Exception.Create('Insert failed on this page');

  // Position it: move 200 units right, 500 up, at 70% scale.
  M := TPdfMatrix.Create;
  try
    M.Scale(0.7, 0.7);
    M.Translate(200, 500);
    RawM := M.Handle;
    if FPDFPageObj_SetMatrix(PageObj, RawM) = 0 then
      raise Exception.Create('Cannot assign the stamp matrix');
  finally
    M.Free;
  end;
  Dest.UpdatePage;   // commit this page's edits to its content stream
  // if not Dest.SaveAs(...) then ... when every page is done.
end;

İki ev temizliği (housekeeping) detayı bunu güvenli (safe) kılar. Birincisi, eklendikten (inserted) sonra (once), sayfa nesnesi XObject'e değil sayfaya aittir (belongs to). XObject'i daha sonra (later) serbest bırakmak (Freeing), daha önce (already) yaptığınız (made) yerleşimleri (placements) geçersiz (invalidate) kılmaz. Aşağıda açıklanan (described) oluştur-yerleştir-serbest-bırak (create-place-free) sıralamasının (ordering) çalışmasını sağlayan şey (what lets) budur. İkincisi, ekleme (inserting) ve konumlandırma (positioning) yalnızca (only) sayfanın bellekteki (memory) nesne listesini (object list) değiştirir (changes); UpdatePage, bu listeyi sayfanın (page's) içerik akışına (content stream) geri (back) serileştiren (serialises) şeydir, bu nedenle onu çağırmadan (calling) düzenlediğiniz (edit) bir sayfa (page), sanki damga (stamp) hiç yerleştirilmemiş (never placed) gibi kaydedilir (saves)

İnsanları (people) ısıran (bites) tanıtıcı yaşam süresi (handle lifetime) kuralı

XObject tanıtıcısını (handle) iki (Two) kısıtlama (constraints) yönetir (govern) ve her ikisini de görmezden gelmek (ignoring either), nedeniyle (cause) alakasız (unrelated) görünen bir başarısızlık (failure) üretir (produces). Birincisi, CreateXObjectFromPage'i çağırdığınız anda kaynak (source) belge aktif (active) olmalıdır (must be). Yakalama (capture), kaynak (source) sayfanın içeriğini canlı (live) kaynak belgeden okur (reads), bu nedenle (so) tanıtıcı (handle) oluşturulduğunda o belgenin (document) ve sayfasının (page) açık ve geçerli olması (valid) gerekir (have to be). İkincisi, ki (and) bu insanları (people) şaşırtan (surprises) kısıtlamadır (one), kaynak sayfa (source page) kapatılmadan (closed) ve pratikte ondan (it came from) gelen (came from) kaynak belgeyi kapatmadan (close) veya serbest bırakmadan (free) önce tanıtıcının (handle) serbest bırakılması (freed) gerekir (must be)

Bunun nedeni (reason is that), XObject'in kaynak (source) belgenin hala (still) sahip olduğu (owns) yapıya (structure) bir referans (reference) olmasıdır. Kaynak (source) gittikten (gone) sonra etrafta (around) taşıyabileceğiniz (carry) kopuk (detached), kendi kendine yeten (self-contained) bir kopya (copy) değildir. Önce (first) kaynağı (source) kapatın; tanıtıcı (handle), parçalanmış (torn down) bir içeriğe (content) işaret eder (pointing) durumda (left) bırakılır, bu nedenle (so) onu daha sonra (later) serbest bırakmak (freeing) veya başka herhangi (any other) bir şekilde (use) kullanmak, artık (no longer) geçerli olmayan (valid) bellek (memory) üzerinde çalışır (operates). Belirti (symptom), sallanan (dangling) bir tanıtıcı (handle) için klasik olanıdır (classic one): kapanışta (shutdown) bir erişim ihlali (access violation) veya tahsis (allocation) sırasına bağlı olarak (depending on) yer (around) değiştiren (moves) aralıklı (intermittent) bir bozulma (corruption); yığın (stack), aslında soruna (problem) neden olan (caused) satır (line) yerine temizleme (cleanup) kodunu (code) işaret eder (points at). Düzeltme (fix) sıralamadır (ordering), savunmacı (defensive) kodlama (coding) değildir. XObject'i oluşturun (Build), ihtiyacı (needs) olan her sayfaya (every page) ekleyin (insert), XObject'i serbest bırakın (free) ve ancak (only) o zaman (then) kaynak (source) belgeyi kapatın (close). TPdfXObject yıkıcısı (destructor), altta yatan (underlying) PDFium tanıtıcısını sizin için (for you) serbest bırakır (releases), bu nedenle (so) sarmalayıcıyı (wrapper) doğru (right) zamanda (time) serbest bırakmak (freeing), sorumluluğunuzun (responsibility) tamamıdır (whole of)

Matris (matrix) ve altı sayısının ne (what) anlama geldiği (mean)

Yerleşim (Placement), PDF'in içeriği (content) konumlandırmak (positioning) için her yerde (everywhere) kullandığı (uses) aynı olan (same one) 2 boyutlu (2D) bir afin dönüşümdür (affine transform) (ISO 32000-1, bölüm 8.3.4). a, b, c, d, e, f olarak yazılan (written) altı (six) sayıdır (numbers) ve PDFium bunları FS_MATRIX kaydı (record) olarak sunar (exposes). Bir noktayı (point) nesnenin kendi uzayından (own space) sayfa uzayına (page space) eşlerler (map):

// x' = a*x + c*y + e
// y' = b*x + d*y + f
//
// a, d : horizontal and vertical scale
// b, c : the shear / rotation terms
// e, f : translation (where the origin lands on the page)

Bu (those) altı (six) değeri elle (by hand) doldurabilirsiniz (fill), ancak (but) bunları (them) elle (by hand) birleştirmek (composing), döndürmenin (rotation) yanlış gittiği (goes wrong) yerdir, çünkü (because) döndürme a, b, c, d'nin dördünü de (all four) birbirine (together) karıştırır (mixes). FPdfMatrix biriminden (unit) gelen (from) TPdfMatrix sarmalayıcısı (wrapper), ortak (common) işlemleri (operations) sizin için birleştirir (composes) ve ilerledikçe (as it goes) sonradan çarpar (post-multiplies), böylece Translate, Scale ve Rotate, onları (them) çağırdığınız (call) sırayla (order) zincirlenir (chain). Çapraz (diagonal) bir filigran (watermark), bir döndürme (rotate) ve ardından (followed by) onu yeniden ortalamak (recentre) için bir çeviridir (translate); köşedeki bir logo (corner logo), bir ölçek (scale) ve ardından bir çeviridir. Matris (matrix) hazır (ready) olduğunda, ham (raw) değerini (value), yani FS_MATRIX türündeki Handle özelliğini, yerel (local) bir değişkene (variable) kopyalayın (copy) ve bunu FPDFPageObj_SetMatrix'e geçirin (pass); içe aktarma (import), matrisi (matrix) bir var parametresi olarak (as) bildirir (declares), bu nedenle bir özellik ona (to it) doğrudan (directly) verilemez (cannot be handed) ve başarısızlık (failure) durumunda sonucu (result) 0'dır. Altı (six) değeri doğrudan (directly) çiftler (doubles) olarak alan alt düzey (lower-level) FPDFPageObj_Transform, bir sarmalayıcı (wrapper) oluşturmak yerine (rather) sayıları (numbers) geçirmeyi (pass) tercih ettiğinizde (when) mevcuttur (available)

Doğru sırayla, her (every) sayfayı (page) damgalamak (Stamping)

Tam (full) desen (pattern), parçaları (pieces) yaşam süresi kuralının (lifetime rule) talep ettiği (demands) sıralamayla (ordering) bir (together) araya getirir (puts). Her iki (both) belgeyi de açın (Open), damgayı (stamp) bir kez (once) yakalayın (capture), 1-tabanlı PageNumber'ı sırayla (in turn) ayarlayarak (setting) hedef sayfaları dolaşın (walk), bir (a) kopyayı (copy) ekleyip (inserting) konumlandırın (positioning), UpdatePage ile her (each) sayfayı (page) kaydedin (committing), ardından XObject'i serbest bırakın (free), ardından (then) SaveAs ile (with) kaydedin (save) ve (and) kaynak belgenin (source document) son (last) kapanmasına (close) izin verin (let)

procedure StampEveryPage(const ASource, AStamp, AOutput: string);
var
  Dest, Stamp: TPdf;
  XObject: TPdfXObject;
  PageObj: FPDF_PAGEOBJECT;
  M: TPdfMatrix;
  RawM: FS_MATRIX;
  I: Integer;
begin
  Dest := TPdf.Create(nil);
  Stamp := TPdf.Create(nil);
  try
    Dest.FileName := ASource;
    Dest.Active := True;
    Stamp.FileName := AStamp;
    Stamp.Active := True;
    if not (Dest.Active and Stamp.Active) then
      raise Exception.Create('Could not open the input documents');

    // 1. Capture the artwork once. Stamp is Active here.
    XObject := Dest.CreateXObjectFromPage(Stamp, 0);
    if XObject = nil then
      raise Exception.Create('Could not capture the stamp page');
    try
      // 2. Place a copy on every page of Dest. PageNumber is 1-based.
      for I := 1 to Dest.PageCount do
      begin
        Dest.PageNumber := I;                // make page I current
        PageObj := Dest.InsertFormObjectFromXObject(XObject);
        if PageObj = nil then
          Continue;

        M := TPdfMatrix.Create;
        try
          M.Rotate(45);                      // diagonal watermark
          M.Translate(150, 100);             // nudge into position
          RawM := M.Handle;
          FPDFPageObj_SetMatrix(PageObj, RawM);
        finally
          M.Free;
        end;
        Dest.UpdatePage;                     // commit this page's edits
      end;
    finally
      XObject.Free;                          // 3. free BEFORE Stamp closes
    end;

    // 4. Write the result while Dest is still open.
    if not Dest.SaveAs(AOutput) then
      raise Exception.Create('Could not save ' + AOutput);
  finally
    Stamp.Free;                              // source closes last
    Dest.Free;
  end;
end;

try bloklarının (blocks) şekli (shape), asıl işi (real work) yapıyor (is doing). İçteki (inner) finally, denetim (control), Stamp'i serbest bırakan (frees) dıştaki (outer) finally'ye ulaşmadan (can ever reach) önce XObject'i serbest bırakır (frees), bu nedenle (so) döngü ortasında (mid-loop) bir istisna (exception) ateşlense (fires) bile (even if), kaynağı (source) hala (still) canlıyken (alive) tanıtıcı (handle) her zaman (always) bırakılır (released). Bu (that) içi içe geçmeyi (nesting) doğru (right) yapın (Get) ve (and) yaşam süresi (lifetime) kuralı (rule) kendi (itself) icabına (takes care of) bakar

Damgalama (Stamping), sayfa (page) içeriğini (content) oluşturmak (building) ve düzenlemek (editing) için daha büyük (larger) bir araç setinin (toolkit) bir köşesidir (corner). Damganız (stamp) yakalanmış (captured) bir sayfa yerine kendi başına (itself) bir görüntü (image) ise, PDFium ile görüntüleri PDF belgelerine dönüştürme makalesi, öncelikle bu bit eşlemi (bitmap) bir belgeye almayı (getting) kapsar (covers). Görünür damganın (visible stamp) yanında (alongside) taşımak (carry) istediğiniz şey (thing) sayfadaki (page) mürekkep (ink) yerine bir dosya (file) olduğunda ise, Delphi'de PDF ekleriyle çalışma makalesi gömülü dosya (embedded-file) tarafını (side) gösterir (shows). Bunların tümü (All of it), bu blog genelinde (elsewhere on this blog) ele alınan (covered) oluşturma (rendering), düzenleme (editing) ve belge (document) API'lerinin yanı sıra (alongside) Delphi ve C++Builder için PDFium Bileşeni ile (with) birlikte gelir (ships)