📜 ⬆️ ⬇️

iText: write to PDF in Russian

Task: create a PDF-document using the library iText . In this case, the user must set the fonts used from the installed ones. Fonts like TrueType and Type1.
IText has a static class that provides access to the FontFactory system fonts. When you receive a font, you must correctly specify its encoding. There are problems. For the TT and T1 encodings are different, and in .NET there are no standard tools to distinguish the TT font from T1.


The font type is stored in the tmPitchAndFamily field in the TEXTMETRIC structure.
Access to this structure can be obtained using the function GetTextMetrics from the library Gdi32.dll.
Making a wrapper to access this function:

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 .

')
So, the font type is determined.
Now we set the correct encoding:
TrueType - BaseFont.IDENTITY_H,
Type1 - "Cp1251".
For the convenience of accessing the necessary fonts, we make another wrapper. At the same time, add caching, as well as getting the font from the factory is very slow.

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 .


And actually use:

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 .


The code is tested on new multilingual and old TrueType, and on old Type1 fonts.
If there are multilingual Type1 fonts, I would like to know how this code will cope with them.
Also, can someone know how to distinguish TT from T1 without using unmanaged code?

Source: https://habr.com/ru/post/113984/


All Articles