Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Нарисовать разряженную строку

1.8K
23 апреля 2011 года
NextTime
217 / / 19.12.2007
Здравствуйте.
Имеется текст, имеется картинка. Я рисую текст на картинке, отступив справа и снизу определённое количество пикселей. Для этого я использую Graphics.MeasureString() и Graphics.DrawString().
Но сейчас появилась задача нарисовать текст разряженно (изменять отступ между буквами). Решить эту проблему с помощью TextRenderer.MesureString() + TextRenderer.DrawText() или Graphics.MeasureString() + Graphics.DrawString() стандартными средствами не удалось. Пытаюсь посимвольно узнавать ширину каждого символа, делать отступ и рисовать, но MeasureString() и MeasureText() дают не совсем верную ширину символа, они дают её с какими-то отступами, но я хочу следить за отступами сам.
Как мне можно нарисовать строку так, чтобы можно было регулировать разряжённость (отступы между буквами)? Или как можно узнать высоту и ширину символа без отступов?
Заранее спасибо.
297
24 апреля 2011 года
koodeer
1.2K / / 02.05.2009
Судя по Википедии, разрядка не популярна в английской типографике, поэтому не реализована в пиндосском софте. Нам нужна расово-верная православная ось с поддержкой р а з р я д к и на уровне ядра! :)

Теперь к делу.
Погуглив, нашёл это: http://stackoverflow.com/questions/2969143/c-drawstring-letter-spacing. Второй ответ в теме, казалось бы, даёт ответ.
Код:
[DllImport("gdi32.dll", CharSet=CharSet.Auto)]
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 - заработало.
Код:
public void Draw(Graphics g)
{
    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);
}

Однако, не обошлось без косяков. Если в тексте восклицательные знаки, то получается фигня.


На этом свои эксперименты закончу.
Будь другом, - если что-нибудь полезное найдёшь, поймёшь как избежать косяков, - отпишись.
5
24 апреля 2011 года
hardcase
4.5K / / 09.08.2005
Цитата: koodeer

На этом свои эксперименты закончу.
Будь другом, - если что-нибудь полезное найдёшь, поймёшь как избежать косяков, - отпишись.



Не мудрено что модификация 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.

1.8K
24 апреля 2011 года
NextTime
217 / / 19.12.2007
Цитата: hardcase
(Возможно, что GetHdc просто создает новый контекст вывода - частичную копию существующего).
Чтобы измененный контекст работал, нужно его создавать руками (назначать нужный шрифт, который также можно создать руками посредством CreateFontIndirect), а сам Graphics создать посредством FromHdc.


Можно, пожалуйста, Ваш пример кода?
У меня Graphics получается из Bitmap и нужно, чтобы я мог узнать размер строки (MesureString).

297
25 апреля 2011 года
koodeer
1.2K / / 02.05.2009
Цитата: hardcase
Не мудрено что модификация hdc средствами API у Graphics не сработала, в документации к GetHdc об этом сказано.

(Возможно, что GetHdc просто создает новый контекст вывода - частичную копию существующего).


Да, я примерно так и думал. Позже вспомнил, что GDI является statefull, а GDI+ использует stateless модель: http://www.rsdn.ru/?article/gdi/gdiplus3.xml


NextTime, а чем не устраивает пример с SetTextCharacterExtra ? В выводимом тексте используются символы, которые обрабатываются неправильно, типа восклицательного знака?


Я тут ещё покопался. Попробовал использовать функцию ExtTextOut. Восклицательные знаки и прочие символы воспринимаются нормально.

Код:
[DllImport("gdi32.dll", EntryPoint = "ExtTextOutW")]
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. И что до сих пор нет управляемых обёрток над всем этим делом.
1.8K
25 апреля 2011 года
NextTime
217 / / 19.12.2007
Цитата: koodeer
NextTime, а чем не устраивает пример с SetTextCharacterExtra ? В выводимом тексте используются символы, которые обрабатываются неправильно, типа восклицательного знака?


На MeasureString и MeasureText не влияет, размер выдают они неверный

Цитата: koodeer
Я тут ещё покопался. Попробовал использовать функцию ExtTextOut. Восклицательные знаки и прочие символы воспринимаются нормально.
Но есть существенное отличие от SetTextCharacterExtra: там задаётся промежуток между символами, а в ExtTextOut задаются расстояния от начала одного символа до начала другого.


Муторненько немного... Опять же на Measure не повлияет...

1.8K
02 мая 2011 года
NextTime
217 / / 19.12.2007
Есть еще идеи?
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог