Pull to refresh

iText: пишем в PDF по-русски

Reading time 5 min
Views 24K
Задача: создавать PDF-документ с использованием библиотеки iText. При этом пользователь должен сам задавать используемые шрифты, из установленных в системе. Шрифты как TrueType так и Type1.
В iText есть статический класс предоставляющий доступ к системным шрифтам FontFactory. При получении шрифта нужно правильно указать его кодировку. Тут и возникли проблемы. Для ТТ и Т1 кодировки разные, и в .NET нет штатных средств позволяющих отличить ТТ-шрифт от Т1.


Тип шрифта хранится в поле tmPitchAndFamily в структуре TEXTMETRIC.
Доступ к этой структуре можно получить с помощью функции GetTextMetrics из библиотеки Gdi32.dll.
Делаем обёртку для доступа к этой функции:

namespace SYS_TEXT {

 using System;
 using System.Drawing;
 using System.Runtime.InteropServices;

 /// <summary> Access to system fon metric class. </summary>
 static class METRIC {

  public static byte TMPF_TRUETYPE = 0x4;

  #region Native structs
  [StructLayout( LayoutKind.Sequential )]
  internal struct TEXTMETRIC {
   public int tmHeight;
   public int tmAscent;
   public int tmDescent;
   public int tmInternalLeading;
   public int tmExternalLeading;
   public int tmAveCharWidth;
   public int tmMaxCharWidth;
   public int tmWeight;
   public int tmOverhang;
   public int tmDigitizedAspectX;
   public int tmDigitizedAspectY;
   public char tmFirstChar;
   public char tmLastChar;
   public char tmDefaultChar;
   public char tmBreakChar;
   public byte tmItalic;
   public byte tmUnderlined;
   public byte tmStruckOut;
   public byte tmPitchAndFamily;
   public byte tmCharSet;
  }
  #endregion // Native structs

  /// <summary> Verify is font TrueType </summary>
  /// <param name="font">Font</param>
  /// <returns>Font is TrueType</returns>
  public static bool FontIsTrueType( Font font ) {
   TEXTMETRIC tm;
   GetFontMetrics( font, out tm );
   return ( tm.tmPitchAndFamily & TMPF_TRUETYPE ) != 0;
  }

  /// <summary> Get font metrics </summary>
  /// <param name="font">Font</param>
  /// <param name="tm">Text metrics</param>
  public static void GetFontMetrics( Font font, out TEXTMETRIC tm ) {
   TEXTMETRIC tmRet = new TEXTMETRIC();
   IntPtr hdc = GetDC( IntPtr.Zero );

   IntPtr hfnt = font.ToHfont();
   // Select in DC new font
   IntPtr hFontPrevious = SelectObject( hdc, hfnt );

   GetTextMetrics( hdc, ref tmRet );
   SelectObject( hdc, hFontPrevious );
   ReleaseDC( IntPtr.Zero, hdc );
   tm = tmRet;
  }

  [DllImport( "Gdi32.dll" )]
  private static extern IntPtr SelectObject( IntPtr hdc, IntPtr hgdiobj );

  [DllImport( "Gdi32.dll" )]
  private static extern bool GetTextMetrics( IntPtr hdc, ref TEXTMETRIC lptm );

  [DllImport( "user32.dll", CharSet = CharSet.Auto )]
  static private extern IntPtr GetDC( IntPtr hWnd );

  [DllImport( "user32.dll", CharSet = CharSet.Auto )]
  static private extern int ReleaseDC( IntPtr hWnd, IntPtr hDC );

 } // METRIC

} // SYS_TEXT


* This source code was highlighted with Source Code Highlighter.


Итак, тип шрифта определили.
Теперь задаём правильную кодировку:
TrueType — BaseFont.IDENTITY_H,
Type1 — «Cp1251».
Для удобства обращения к нужным шрифтам, делаем ещё одну обёртку. Заодно добавим кэширование, так-как получение шрифта из фабрики очень медленное.

namespace iText_font_test {

 using System;
 using System.Collections;
 using iTextSharp.text;
 using iTextSharp.text.pdf;
 using SYS_TEXT;

 /// <summary> Helpers for iTextSharp library </summary>
 public static class ITEXT_HLP {

  #region Properties
  /// <summary> Font cache </summary>
  private static Hashtable          __cache_fonts;
  #endregion // Properties

  #region Methods
  /// <summary> Get font from system fonts </summary>
  /// <param name="font_nm">Font name</param>
  /// <returns>BaseFont</returns>
  public static BaseFont           font_sys_get( string font_nm ) {

   // Create font cache if not exist
   if( null == __cache_fonts )
    __cache_fonts = new Hashtable();

   // Try get font from cache
   if( __cache_fonts.Contains( font_nm ) )
    return (BaseFont) __cache_fonts[ font_nm ];

   BaseFont result_font;

   // Try get font from system
   try {
    var sf = new System.Drawing.Font( font_nm, 8f );
    var enc = METRIC.FontIsTrueType( sf ) ? BaseFont.IDENTITY_H : "Cp1251";
    FontFactory.RegisterDirectories();
    var font = FontFactory.GetFont( font_nm, enc, true );
    result_font = font.GetCalculatedBaseFont( true );
   } catch( Exception ) {
    return null;
   }

   // Save font in cache
   if( null != result_font )
    __cache_fonts[ font_nm ] = result_font;

   return result_font;
  } // font_sys_get
  #endregion // Methods

 } // ITEXT_HLP

} // iText_font_test

* This source code was highlighted with Source Code Highlighter.


И собственно использование:

using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;

namespace iText_font_test {
 class Program {
  static void Main( string[] args ) {

   // Create new PDF document
   Rectangle pagesize = new Rectangle( 600f, 300f );
   Document document = new Document( pagesize, 0f, 0f, 0f, 0f );
   PdfWriter wr_pdf = PdfWriter.GetInstance( document, new FileStream( "font_test.pdf", FileMode.Create ) );
   document.Open();
   PdfContentByte canvas = wr_pdf.DirectContent;

   // Draw text
   canvas.BeginText();
   BaseFont font = ITEXT_HLP.font_sys_get( "arial" );
   canvas.SetFontAndSize( font, 24f );
   canvas.ShowTextAligned( PdfContentByte.ALIGN_LEFT, "Привет, Мир!", 100f, 200f, 0f );
   canvas.EndText();

   // Close document
   document.Close();

  }
 }
}

* This source code was highlighted with Source Code Highlighter.


Код опробован на новых многоязычных и старых TrueType, и на старых Type1 шрифтах.
Если существуют многоязычные Type1 шрифты, хотелось бы узнать как этот код справится с ними.
Так же, может кто-то знает, как можно отличить TT от T1 без использования неуправляемого кода?
Tags:
Hubs:
+6
Comments 5
Comments Comments 5

Articles