Artikel Teknis

Membaca Properti Font PDF dengan PDFium Component di Delphi

Ketika PDF tampak berbeda di mesin yang berbeda, penyebab paling umum adalah font tidak disematkan. Setiap glif dalam PDF membawa properti font yang dapat Anda baca dari Delphi dengan PDFium Component: nama keluarga, nama dasar, ketebalan, sudut miring, dan apakah data glif aktual benar-benar tersimpan dalam file. Setelah Anda dapat memeriksa properti tersebut secara terprogram, diagnosis masalah rendering tipografi menjadi lebih mudah secara signifikan

Properti font dibaca per glif, bukan per halaman

API PDFium Component mengekspos properti font pada level karakter. Setelah membuka dokumen dan mengarahkan ke halaman yang ingin Anda periksa, Anda mengakses properti melalui indeks karakter. Pdf.Character[I] memberikan titik kode Unicode glif di posisi tersebut, dan Pdf.FontFamilyName[I], Pdf.FontBaseName[I], Pdf.FontWeight[I], Pdf.FontItalicAngle[I], serta Pdf.FontEmbedded[I] memberikan metadata font yang sesuai

procedure DescribeFontAt(Pdf: TPdf; CharIndex: Integer);
var
  Report: TStringList;
  PtSize: Single;
begin
  Report := TStringList.Create;
  try
    PtSize := Pdf.FontSize[CharIndex];

    Report.Add('Character : ' + Pdf.Character[CharIndex]);
    Report.Add('Family    : ' + Pdf.FontFamilyName[CharIndex]);
    Report.Add('Base name : ' + Pdf.FontBaseName[CharIndex]);
    Report.Add('Weight    : ' + IntToStr(Pdf.FontWeight[CharIndex]));
    Report.Add('Italic    : ' + FloatToStr(Pdf.FontItalicAngle[CharIndex]));
    Report.Add('Size (pt) : ' + FloatToStr(PtSize));
    Report.Add('Embedded  : ' + BoolToStr(Pdf.FontEmbedded[CharIndex], True));

    ShowMessage(Report.Text);
  finally
    Report.Free;
  end;
end;

Pola yang sama bekerja di dalam loop untuk setiap karakter pada halaman. Namun, biasanya lebih efisien untuk mengumpulkan nama font unik alih-alih membuat laporan untuk setiap karakter individual

Mendeteksi font yang tidak disematkan saat klik mouse

Cara cepat untuk menghubungkan inspeksi font ke UI adalah merespons klik mouse pada TPdfView dan membaca properti di posisi yang diklik. PdfView.CharIndexAt mengonversi koordinat layar ke indeks karakter, yang kemudian dapat Anda masukkan ke dalam properti per-karakter yang disebutkan di atas

procedure TfrmMain.PdfViewMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  CharIdx: Integer;
begin
  if Button <> mbLeft then Exit;
  CharIdx := PdfView.CharIndexAt(X, Y);
  if CharIdx < 0 then Exit;   // clicked empty space

  if not Pdf.FontEmbedded[CharIdx] then
    ShowMessage('Font "' + Pdf.FontBaseName[CharIdx] + '" is NOT embedded')
  else
    ShowMessage('Font "' + Pdf.FontBaseName[CharIdx] + '" is embedded');
end;

Melaporkan semua font yang tidak disematkan dalam dokumen

Untuk audit seluruh dokumen, Anda perlu mengiterasi semua halaman dan semua karakter, mengumpulkan nama font yang tidak disematkan. Menggunakan set atau TDictionary menghindari duplikasi:

procedure ReportNonEmbeddedFonts(Pdf: TPdf; Report: TStrings);
var
  Page, CharIdx: Integer;
  FontName: string;
  Seen: TDictionary<string, Boolean>;
begin
  Seen := TDictionary<string, Boolean>.Create;
  try
    for Page := 1 to Pdf.PageCount do
    begin
      Pdf.PageNumber := Page;
      for CharIdx := 0 to Pdf.CharCount - 1 do
      begin
        if not Pdf.FontEmbedded[CharIdx] then
        begin
          FontName := Pdf.FontBaseName[CharIdx];
          if not Seen.ContainsKey(FontName) then
          begin
            Seen.Add(FontName, True);
            Report.Add('Page ' + IntToStr(Page) + ': ' + FontName);
          end;
        end;
      end;
    end;
  finally
    Seen.Free;
  end;
end;

Menyimpan data font yang disematkan ke disk

PDFium Component juga memungkinkan Anda mengekstrak byte font yang disematkan ke dalam stream. Ini berguna ketika Anda perlu mengaudit apakah font yang disematkan dalam PDF adalah versi yang sama dengan yang ada di sistem Anda, atau ketika Anda ingin mengekstrak font untuk keperluan analisis:

procedure SaveEmbeddedFont(Pdf: TPdf; CharIndex: Integer;
  const OutputPath: string);
var
  Stream: TMemoryStream;
begin
  Stream := TMemoryStream.Create;
  try
    Pdf.GetEmbeddedFont(CharIndex, Stream);
    if Stream.Size > 0 then
      Stream.SaveToFile(OutputPath)
    else
      ShowMessage('No embedded font data at character ' + IntToStr(CharIndex));
  finally
    Stream.Free;
  end;
end;

Selalu periksa Pdf.FontEmbedded[CharIndex] sebelum memanggil GetEmbeddedFont. Ketika font tidak disematkan, stream yang dikembalikan akan kosong, sehingga pemeriksaan tersebut juga menghindari penulisan file output kosong

Untuk mempelajari lebih lanjut tentang rendering dokumen, muat dan tampilkan PDF, lihat PDFium Delphi VCL Component