Нарисовать разряженную строку
Имеется текст, имеется картинка. Я рисую текст на картинке, отступив справа и снизу определённое количество пикселей. Для этого я использую Graphics.MeasureString() и Graphics.DrawString().
Но сейчас появилась задача нарисовать текст разряженно (изменять отступ между буквами). Решить эту проблему с помощью TextRenderer.MesureString() + TextRenderer.DrawText() или Graphics.MeasureString() + Graphics.DrawString() стандартными средствами не удалось. Пытаюсь посимвольно узнавать ширину каждого символа, делать отступ и рисовать, но MeasureString() и MeasureText() дают не совсем верную ширину символа, они дают её с какими-то отступами, но я хочу следить за отступами сам.
Как мне можно нарисовать строку так, чтобы можно было регулировать разряжённость (отступы между буквами)? Или как можно узнать высоту и ширину символа без отступов?
Заранее спасибо.
Теперь к делу.
Погуглив, нашёл это: http://stackoverflow.com/questions/2969143/c-drawstring-letter-spacing. Второй ответ в теме, казалось бы, даёт ответ.
public static extern int SetTextCharacterExtra(
IntPtr hdc, // DC handle
int nCharExtra // extra-space value
);
public void Draw(Graphics g)
{
IntPtr hdc = g.GetHdc();
SetTextCharacterExtra(hdc, 24); //set spacing between characters
g.ReleaseHdc(hdc);
g.DrawString("str",this.Font,Brushes.Black,0,0);
}
Однако, у меня этот код разрядку не меняет.
Попробовал заменить Graphics.DrawString на TextRenderer.DrawText - заработало.
{
IntPtr hdc = g.GetHdc();
SetTextCharacterExtra(hdc, 12); //set spacing between characters
g.ReleaseHdc(hdc);
TextRenderer.DrawText(g, "Hello world", Font, new Point(0, 0), Color.Black);
TextRenderer.DrawText(g, "Hello! world!", Font, new Point(0, 30), Color.Black);
TextRenderer.DrawText(g, "Привет мир", Font, new Point(0, 60), Color.Black);
TextRenderer.DrawText(g, "Привет! мир!", Font, new Point(0, 90), Color.Black);
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
Draw(e.Graphics);
}
Однако, не обошлось без косяков. Если в тексте восклицательные знаки, то получается фигня.
На этом свои эксперименты закончу.
Будь другом, - если что-нибудь полезное найдёшь, поймёшь как избежать косяков, - отпишись.
На этом свои эксперименты закончу.
Будь другом, - если что-нибудь полезное найдёшь, поймёшь как избежать косяков, - отпишись.
Не мудрено что модификация hdc средствами API у Graphics не сработала, в документации к GetHdc об этом сказано.
[quote=MSDN]
Calls to the GetHdc and ReleaseHdc methods must appear in pairs. During the scope of a GetHdc and ReleaseHdc method pair, you usually make only calls to GDI functions. Calls in that scope made to GDI+ methods of the Graphics that produced the hdc parameter fail with an ObjectBusy error. Also, GDI+ ignores any state changes made to the Graphics of the hdc parameter in subsequent operations.
[/quote]
(Возможно, что GetHdc просто создает новый контекст вывода - частичную копию существующего).
Чтобы измененный контекст работал, нужно его создавать руками (назначать нужный шрифт, который также можно создать руками посредством CreateFontIndirect), а сам Graphics создать посредством FromHdc.
Чтобы измененный контекст работал, нужно его создавать руками (назначать нужный шрифт, который также можно создать руками посредством CreateFontIndirect), а сам Graphics создать посредством FromHdc.
Можно, пожалуйста, Ваш пример кода?
У меня Graphics получается из Bitmap и нужно, чтобы я мог узнать размер строки (MesureString).
(Возможно, что GetHdc просто создает новый контекст вывода - частичную копию существующего).
Да, я примерно так и думал. Позже вспомнил, что GDI является statefull, а GDI+ использует stateless модель: http://www.rsdn.ru/?article/gdi/gdiplus3.xml
NextTime, а чем не устраивает пример с SetTextCharacterExtra ? В выводимом тексте используются символы, которые обрабатываются неправильно, типа восклицательного знака?
Я тут ещё покопался. Попробовал использовать функцию ExtTextOut. Восклицательные знаки и прочие символы воспринимаются нормально.
static extern bool ExtTextOut(
IntPtr hdc,
int x, int y,
int options,
[In] ref Rectangle rect,
[MarshalAs(UnmanagedType.LPWStr)] string text,
int textLength,
[In] int[] distance);
[DllImport("gdi32.dll", ExactSpelling = true, PreserveSig = true, SetLastError = true)]
static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
[DllImport("gdi32.dll")]
static extern uint SetTextColor(IntPtr hdc, int color);
[DllImport("gdi32.dll")]
static extern uint SetBkColor(IntPtr hdc, int color);
private void Form1_Paint(object sender, PaintEventArgs e)
{
IntPtr hdc = e.Graphics.GetHdc();
SelectObject(hdc, Font.ToHfont());
SetTextColor(hdc, ColorTranslator.ToWin32(Color.Black));
SetBkColor(hdc, ColorTranslator.ToWin32(SystemColors.Control));
// Массив расстояний от одного знакоместа до другого
int[] arr = { 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 };
Rectangle rect = new Rectangle(); // фактически не используется
string str = "Hello world!";
ExtTextOut(hdc, 0, 0, 0, ref rect, str, str.Length, arr);
e.Graphics.ReleaseHdc(hdc);
}
Но есть существенное отличие от SetTextCharacterExtra: там задаётся промежуток между символами, а в ExtTextOut задаются расстояния от начала одного символа до начала другого.
Информацию, как правильно описать функции искал на http://pinvoke.net/
ЗЫ: жаль конечно, что в GDI+ используются не все возможности GDI. И что до сих пор нет управляемых обёрток над всем этим делом.
На MeasureString и MeasureText не влияет, размер выдают они неверный
Но есть существенное отличие от SetTextCharacterExtra: там задаётся промежуток между символами, а в ExtTextOut задаются расстояния от начала одного символа до начала другого.
Муторненько немного... Опять же на Measure не повлияет...