이번에 설명할 주제는 바이트 배열(또는 스트림)로부터 폰트를 만드는 방법에 대해서 설명드리도록 하겠습니다.

저는 이 방법을 꽤나 많이 사용하는데요. 제가 설명드리는 방법에는 이런 장점들이 있습니다.

  • 사용자 컴퓨터에 폰터가 설치되어 있지 않을 경우에도 프로그램에 설정된 폰트를 사용할 수 있습니다.
  • API 선언이 필요 없습니다.
  • 리소스에 포함만 시키면 끝. 필요한건 바이트 배열 또는 스트림만 있으면 됩니다.
  • 추가 라이브러리가 필요 없습니다.
  • .NET Framework 2.0 부터 지원되기 때문에 .NET Framework를 따로 설치하지 않은 윈도우 7 운영체제와 호환!

단점이라면, 이정도를 꼽을 수 있겠습니다.
  • 프로그램의 크기에 폰트 크기가 더해집니다.
  • 폰트를 적용하려면 약간의 코드 수정이 필요합니다.

그래도, 사용자에게 편리한 환경을 제공할 수만 있다면 단점은 감수할 수 있겠죠.


이 방법을 사용하기 위해선 두 개의 네임스페이스를 포함시켜야 합니다.
(포함시키지 않아도 상관없지만, 네임스페이스를 계속 쓰는것 만큼 귀찮은 일도 없겠죠 ㅋㅋ)

System.Runtime.InteropServices
System.Drawing.Text


두 개의 네임스페이스에서 사용할 클래스도 두 개 입니다.

바로, Marshal 클래스와 PrivateFontCollection 클래스입니다. 각 클래스의 참고 문서입니다!

Marshal 클래스 (MSDN)

PrivateFontCollection 클래스 (MSDN)


사용할 클래스들의 문서를 자세히 보셨다면 감이 잡히는 분들도 계시겠고, 알 수 없는 분들도 계실 것 같네요!

이 메서드들을 사용할 것입니다.

Marshal.AllocHGlobal
Marshal.Copy
Marshal.FreeHGlobal
PrivateFontCollection.AddMemoryFont


너무 간단하지 않나요? 네 개의 메서드를 이용하면? 바로 끝입니다 ^^


어떻게 하는지 빨리 알려달라구요?

알겠습니다! 그럼 바로 코드를 살펴보도록 하죠!

public static class CSharpTechnique {
    /// <summary>
    /// 바이트 배열로부터 폰트 패밀리를 만듭니다.
    /// </summary>
    /// <param name="byteArray">폰트 패밀리를 만드는데 사용할 바이트 배열을 입력합니다.</param>
    public static FontFamily FontFamilyFromByte(Byte[] byteArray) {
            
        // 메모리 폰트를 저장하기 위한 컬렉션입니다.
        PrivateFontCollection pfCollection = new PrivateFontCollection();

        // 바이트 배열을 복사하기 위한 포인터를 할당합니다.
        IntPtr pFont = Marshal.AllocHGlobal(byteArray.Length);

        // 메모리 할당에 실패하면 null 반환합니다.
        // AllocHGlobal 메서드를 호출했을 때 할당에 실패하면 내부적으로 예외를 발생시키게 되어있지만
        // 추가로 처리를 해줍니다.
        if ( pFont == IntPtr.Zero )
            return null;

        // 포인터에 바이트 배열을 복사합니다.
        Marshal.Copy(byteArray, 0, pFont, byteArray.Length);

        // 폰트 컬렉션에 메모리 폰트를 추가합니다.
        // 꼭! 배열의 길이를 정확하게 명시해줘야 합니다.
        pfCollection.AddMemoryFont(pFont, byteArray.Length);

        // 메모리 폰트를 추가하는 작업이 완료되었으면,
        // 할당한 메모리 공간을 해제하여 메모리 낭비가 없도록 합니다.
        Marshal.FreeHGlobal(pFont);

        // 추가를 했는데도 길이가 0이면 null 을 반환합니다.
        if ( pfCollection.Families.Length == 0 )
            return null;

        // 폰트 패밀리를 반환합니다.
        return pfCollection.Families[0];
    }

    /// <summary>
    /// 바이트 배열로부터 폰트를 만듭니다.
    /// </summary>
    /// <param name="byteArray">폰트를 만드는데 사용할 바이트 배열을 입력합니다.</param>
    /// <param name="emSize">폰트의 크기를 입력합니다.</param>
    public static Font FontFromByte(Byte[] byteArray, Single emSize) {
        return FontFromByte(byteArray, emSize, FontStyle.Regular);
    }
    /// <summary>
    /// 바이트 배열로부터 폰트를 만듭니다.
    /// </summary>
    /// <param name="byteArray">폰트를 만드는데 사용할 바이트 배열을 입력합니다.</param>
    /// <param name="emSize">폰트의 크기를 입력합니다.</param>
    /// <param name="style">폰트의 스타일을 입력합니다.</param>
    public static Font FontFromByte(Byte[] byteArray, Single emSize, FontStyle style) {
        FontFamily ff = FontFamilyFromByte(byteArray);

        // 폰트 패밀리를 만들지 못했으면 null 반환!
        if (ff == null)
            return null;

        // 폰트를 만들고 반환합니다.
        return new Font(ff, emSize, style);
    }
}


이해하기 쉽도록 주석을 달아놨습니다.

PrivateFontCollection 클래스를 이용해도 바로 폰트를 만들 수 없고, 폰트 패밀리를 만드는 것이므로 폰트 패밀리를 이용하여 폰트를 만들어 줘야 합니다.

만드는 단계는 이렇게 표현됩니다.

  1. 포인터를 바이트 배열의 크기만큼 할당합니다.
  2. 메모리가 할당된 포인터에 바이트 배열을 복사합니다.
  3. AddMemoryFont 메서드를 호출하여 PrivateFontCollection 클래스의 컬렉션에 폰트 패밀리를 추가합니다.
  4. 포인터를 메모리에서 해제합니다.
  5. 컬렉션에 추가된 폰트 패밀리를 이용하여 폰트를 만듭니다.

예제 프로그램의 실행 화면입니다. (대상 폰트는 나눔고딕코딩 폰트입니다)


클래스 파일 및 예제 프로젝트 파일:

CSharpTechnique.cs

testsuite.zip



긴 글 읽어주셔서 감사합니다~^^

+ Recent posts