Graphics.measurestring ()가 예상 숫자보다 더 높은 이유는 무엇입니까?
-
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
:
GDI+: TextRenderingHintAntiAlias
, GDI: ANTIALIASED_QUALITY
:
GDI+: TextRenderingHintAntiAliasGridFit
, GDI: 지원되지 않고 antialiased_quality를 사용합니다:
GDI+: TextRenderingHintSingleBitPerPixelGridFit
, GDI: PROOF_QUALITY
:
GDI+: TextRenderingHintSingleBitPerPixel
, GDI: DRAFT_QUALITY
:
나는 그것이 이상하다고 생각합니다 DRAFT_QUALITY
동일합니다 PROOF_QUALITY
, 그것은 동일합니다 CLEARTYPE_QUALITY
.
또한보십시오
다른 팁
크기 = 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);
}