Graphics.measurestring ()가 예상 숫자보다 더 높은 이유는 무엇입니까?

StackOverflow https://stackoverflow.com/questions/1203087

  •  05-07-2019
  •  | 
  •  

문제

영수증을 생성하고 있으며 그래픽 객체를 사용하여 DrawString 메소드를 호출하여 필요한 텍스트를 인쇄하고 있습니다.

graphics.DrawString(string, font, brush, widthOfPage / 2F, yPoint, stringformat);

이것은 내가해야 할 일에 잘 맞습니다. 나는 항상 내가 인쇄하는 것을 알고 있었기 때문에 모든 문자열을 수동으로 다듬을 수 있도록 80mm 영수증 종이에 올바르게 맞출 수있었습니다. 그런 다음 이것을보다 유연하게 만드는 추가 기능을 추가해야했습니다. 사용자는 바닥에 추가되는 문자열을 통과 할 수 있습니다.

그들이 무엇을 넣을지 몰랐기 때문에, 나는 줄을 랩하고 문자열 자체를 사용하는 많은 캐릭터를 사용하는 내 자신의 단어 랩 함수를 만들었습니다. 캐릭터 수를 찾기 위해 다음과 같은 일을하고있었습니다.

float width = document.DefaultPageSettings.PrintableArea.Width;
int max = (int)(width / graphics.MeasureString("a", font).Width);

이제 너비가 283으로 돌아오고 있으며, 이는 mm의 약 72인데, 이는 80mm 종이의 여백을 설명 할 때 의미가 있습니다.

그러나 측정 방법은 Courier New 8pt 글꼴에서 10.5를 반환합니다. 그래서 내가 36-40으로 예상되는 것을 돌아 다니는 대신 26을 얻었으므로 2 줄의 텍스트가 3-4로 바뀌 었습니다.

PrintableArea.width의 단위는 1/100 인치이며 그래픽 객체의 페이지 유닛은 디스플레이입니다 (일반적으로 프린터의 경우 1/100 인치라고 표시됩니다). 그렇다면 왜 26 번만 돌아 오는가?

도움이 되었습니까?

해결책

WindowsClient.net에서 :

GDI+는 표시되는 모든 문자열의 각 끝에 소량 (1/6 EM)을 추가합니다. 이 1/6 EM은 돌출 된 끝이있는 글리프를 허용합니다 (예 : 이탤릭체)에프'), 또한 GDI+에게 그리드 피팅 확장에 도움이되는 소량의 여유를 제공합니다.

기본 조치 DrawString 인접한 달리기를 표시하는 데 도움이됩니다.

  • 먼저 기본 stringformat은 각 출력의 각 끝에 추가 1/6 EM을 추가합니다.
  • 둘째, 그리드 장착 너비가 설계된 것보다 적을 때, 문자열은 EM까지 계약 할 수 있습니다.

이러한 문제를 피하기 위해 :

  • 항상 통과하십시오 MeasureString 그리고 DrawString 타이포그래피 stringformat을 기반으로 한 stringformat (GenericTypographic).
    그래픽을 설정하십시오 TextRenderingHint 에게 TextRenderingHintAntiAlias. 이 렌더링 방법은 안티 앨리어싱 및 서브 픽셀 글리프 포지셔닝을 사용하여 그리드 적합의 필요성을 피하기 때문에 본질적으로 독립적입니다.

.NET에는 텍스트를 그리는 두 가지 방법이 있습니다.

  • gdi+ (graphics.MeasureString 그리고 graphics.DrawString)
  • GDI (TextRenderer.MeasureText 그리고 TextRenderer.DrawText)

Michael Kaplan의 (RIP) 훌륭한 블로그에서 모든 것을 정렬합니다, .NET 1.1에서 사용 된 모든 것 GDI+ 텍스트 렌더링의 경우. 그러나 몇 가지 문제가있었습니다.

  • GDI+의 다소 무국적 특성으로 인한 몇 가지 성능 문제가 있으며, 여기서 장치 컨텍스트가 설정되고 각 호출 후 원본이 복원됩니다.
  • 국제 텍스트를위한 형성 엔진은 Windows/Uniscribe 및 Avalon (Windows Presentation Foundation) 용으로 여러 번 업데이트되었지만 GDI+에 대해 업데이트되지 않았으므로 새로운 언어에 대한 국제적 렌더링 지원이 동일한 수준의 품질을 갖지 못하게합니다.

그래서 그들은 .NET 프레임 워크를 변경하여 사용을 중지하고 싶다는 것을 알았습니다. GDI+텍스트 렌더링 시스템 및 사용 GDI. 처음에 그들은 단순히 변화 할 수 있기를 바랐습니다.

graphics.DrawString

오래된 것을 부르기 위해 DrawText GDI+대신 API. 그러나 그들은 GDI+가 한 것과 같이 텍스트 포장 및 간격을 정확하게 일치시킬 수 없었습니다. 그래서 그들은 유지해야했습니다 graphics.DrawString GDI+ (호환성 이유; graphics.DrawString 갑자기 그들의 텍스트가 예전 방식으로 포장되지 않았다는 것을 알게 될 것입니다).

새로운 정적 TextRenderer GDI 텍스트 렌더링을 마무리하기 위해 클래스가 만들어졌습니다. 두 가지 방법이 있습니다.

TextRenderer.MeasureText
TextRenderer.DrawText

메모: TextRenderer GDI 주변의 포장지입니다 graphics.DrawString 여전히 GDI+주변의 래퍼입니다.


그런 다음 모든 기존 .NET 컨트롤과 관련하여 무엇을 해야하는지에 대한 문제가있었습니다.

  • Label
  • Button
  • TextBox

그들은 사용하기 위해 그들을 뒤집기를 원했습니다 TextRenderer (예 : GDI), 그러나 그들은 조심해야했습니다. .NET 1.1에서와 같이 자신의 컨트롤 그리기에 의존하는 사람들이있을 수 있습니다. 그리고 태어났습니다. "호환 텍스트 렌더링".

기본적으로 응용 프로그램의 컨트롤은 .NET 1.1에서했던 것처럼 행동합니다 (그들은”호환 가능").

끄다 호환성 모드를 호출하여 :

Application.SetCompatibleTextRenderingDefault(false);

이것은 더 나은 국제 지원으로 응용 프로그램을 더 빠르고 빠르게 만듭니다. 요약 :

SetCompatibleTextRenderingDefault(true)  SetCompatibleTextRenderingDefault(false)
=======================================  ========================================
 default                                  opt-in
 bad                                      good
 the one we don't want to use             the one we want to use
 uses GDI+ for text rendering             uses GDI for text rendering
 graphics.MeasureString                   TextRenderer.MeasureText
 graphics.DrawString                      TextRenderer.DrawText
 Behaves same as 1.1                      Behaves *similar* to 1.1
                                          Looks better
                                          Localizes better
                                          Faster

GDI+ 간의 매핑에 주목하는 것도 유용합니다. TextRenderingHint 그리고 해당 LOGFONT 품질 GDI 글꼴 도면에 사용 :

TextRenderingHint           mapped by TextRenderer to LOGFONT quality
========================    =========================================================
ClearTypeGridFit            CLEARTYPE_QUALITY (5) (Windows XP: CLEARTYPE_NATURAL (6))
AntiAliasGridFit            ANTIALIASED_QUALITY (4)
AntiAlias                   ANTIALIASED_QUALITY (4)
SingleBitPerPixelGridFit    PROOF_QUALITY (2)
SingleBitPerPixel           DRAFT_QUALITY (1)
else (e.g.SystemDefault)    DEFAULT_QUALITY (0)

샘플

다음은 GDI+ (Graphics.DrawString) 구절 GDI (Textrenderer.DrawText) 텍스트 렌더링과 비교합니다.

GDI+: TextRenderingHintClearTypeGridFit, GDI: CLEARTYPE_QUALITY:

enter image description here

GDI+: TextRenderingHintAntiAlias, GDI: ANTIALIASED_QUALITY:

enter image description here

GDI+: TextRenderingHintAntiAliasGridFit, GDI: 지원되지 않고 antialiased_quality를 사용합니다:

enter image description here

GDI+: TextRenderingHintSingleBitPerPixelGridFit, GDI: PROOF_QUALITY:

enter image description here

GDI+: TextRenderingHintSingleBitPerPixel, GDI: DRAFT_QUALITY:

enter image description here

나는 그것이 이상하다고 생각합니다 DRAFT_QUALITY 동일합니다 PROOF_QUALITY, 그것은 동일합니다 CLEARTYPE_QUALITY.

또한보십시오

다른 팁

Courier New Size 11

크기 = 11 인 글꼴 '택배 새'를 만들 때 위의 이미지에서와 같이 출력이 나타납니다. 당신은 높이가 밑줄을 포함하지 않고 14 픽셀임을 알 수 있습니다. 너비는 정확히 14 픽셀 (각 문자마다 7 픽셀)입니다.

따라서이 글꼴은 14x14 픽셀을 렌더링합니다.

하지만 TextRenderer.MeasureText() 대신 21 픽셀의 너비를 반환합니다. 정확한 값이 필요한 경우 이것은 쓸모가 없습니다.

솔루션은 다음 코드입니다.

Font i_Courier = new Font("Courier New", 11, GraphicsUnit.Pixel);

Win32.SIZE k_Size;
using (Bitmap i_Bmp = new Bitmap(200, 200, PixelFormat.Format24bppRgb))
{
    using (Graphics i_Graph = Graphics.FromImage(i_Bmp))
    {
        IntPtr h_DC = i_Graph.GetHdc();
        IntPtr h_OldFont = Win32.SelectObject(h_DC, i_Courier.ToHfont());

        Win32.GetTextExtentPoint32(h_DC, "Áp", 2, out k_Size);

        Win32.SelectObject(h_DC, h_OldFont);
        i_Graph.ReleaseHdc();
    }
}

k_size에는 올바른 크기가 포함됩니다 : 14x14

중요한:이 코드는 정기적 인 글꼴을 올바르게 측정합니다. 이탤릭체 글꼴 (항상 오른쪽에 돌출부가있는)에 대한 정확한 값이 필요한 경우이 기사에서 언급 한 링크를 읽어야합니다. http://www.codeproject.com/articles/14915/width-of-text-in- 이탈리아-폰트

부록:C#에서 API 호출을 사용한 적이없는 사람들을 위해 여기서 클래스 Win32를 만드는 방법을 힌트합니다. 이것은 완전하지 않습니다. 자세한 내용은 살펴보십시오 http://www.pinvoke.net

using System.Runtime.InteropServices;

public class Win32
{       
    [StructLayout(LayoutKind.Sequential)]
    public struct SIZE
    {
        public int cx;
        public int cy;
    }

    [DllImport("Gdi32.dll")]
    public static extern bool GetTextExtentPoint32(IntPtr hdc, string lpString, int cbString, out SIZE lpSize);

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

다음은 작동 방식을 이해하는 데 도움이되는 설명입니다. 그리고 각 캐릭터 전후의 공간을 원인하는 원인.

GDI DrawString Configurator 앱

화면 캡처

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top