Точное измерение, рендеринг, тестирование попаданий и печать текста в приложении WinForms
Мы должны:
- Измерьте текст точно.
- Рендеринг текста построчно в графический контекст экрана при наличии преобразований перевода и масштабирования, применяемых к графическому контексту.
- Проверка нажатия: позволяет точно выбирать текст с помощью мыши или отображаемой каретки.
- При необходимости распечатайте результат с максимально возможной точностью на принтере. Примечание: это вторично. Рендеринг экрана и проверка попадания являются первичными.
- Работать на Windows XP и более поздних операционных системах.
в приложении WinForms, которое также отображает графику и изображения в том же графическом контексте.
Есть четыре технологии, с которыми мы столкнулись. Мы попытались использовать первые два и столкнулись с описанными проблемами в течение нескольких месяцев.
GDI +
Предположительно текст, независимый от разрешения. Однако, согласно этому вопросу и другим источникам, этой технологии следует избегать из-за проблем с качеством.
MSDN заявляет, что вызывает Graphics.MeasureString вместе с StringFormat.GenericTypographic
а также TextRenderingHint.AntiAlias
производит точное измерение струны. Однако, по нашему опыту и опыту других, это не так - мы не получаем точных измерений струн.
- Плюсы: быстро
- Минусы: неточное измерение строки.
Результат: невозможно использовать из-за неточного измерения строки.
GDI через TextRenderer
Это было введено, чтобы преодолеть ограничения GDI+. Однако это ввело свои ограничения:
- Очень медленно
- Не работает с графическими преобразованиями
Результат: непригоден по этим причинам
GDI через p/invoke
призвание GetTextExtentExPoint
для измерения текста и DrawText
/ DrawTextEx
/ ExtTextOut
для рендеринга.
Мы еще не пробовали это.
DirectWrite
Это кажется многообещающим, поскольку оно взаимодействует с другими технологиями, включая GDI/GDI+, поэтому, вероятно, остальная часть нашего графического рендеринга не изменится. Однако он доступен только для Windows Vista и более поздних версий Windows. В настоящее время это проблема, поскольку Windows XP все еще имеет значительную установленную базу.
Вопрос
Какие из этих технологий можно заставить работать с учетом требований?
Примечание. В этой теме много дезинформации, поэтому, пожалуйста, отвечайте на этот вопрос, только если у вас есть опыт в этой области. Также, пожалуйста, не предлагайте WPF - это не то, что мы собираемся использовать.
3 ответа
Предположительно MeasureCharacterRanges
функция более точная, чем MeasureString
,
Если вам необходимо настроить таргетинг как на экран, так и на принтер, то вам нужно принять некоторые решения относительно вашего подхода, прежде чем решить, какой механизм рендеринга использовать. Вот несколько возможностей:
- Выложите в единицах экрана и сделайте наилучшее приближение для принтера.
- Разложите в принтерах и сделайте наилучшее приближение для экрана.
- Расположитесь в теоретическом пространстве с высоким разрешением и сделайте наилучшее возможное приближение как для принтера, так и для экрана.
Во всех случаях наилучшее из возможных приближений может быть получено с помощью очень тщательных приближений на каждом шаге, которые могут дать наивысшую точность воспроизведения для каждого устройства за счет точности сопоставления, или вы можете выполнить рендеринг в растровое изображение и масштабирование на устройстве, которое дает меньшую точность, но наилучшее приближение к другому пространству.
Я сделал предварительный просмотр с помощью GDI. Это сложно, но выполнимо для обычных раскладок. Но если вы обрабатываете произвольные преобразования, особенно повороты, отличные от +/-90 градусов, это почти невозможно.
Очень легко совершать тонкие ошибки и полагать, что полученные вами измерения неверны.
Подсказка шрифта делает масштабирование шрифта нелинейным, когда ширина обводки имеет тот же порядок величины, что и значение dpi, поэтому, если вы получаете ширину
w
для некоторого текста ширина, если вы удвоите размер шрифта, может быть не совсем2*w
,Подстановка шрифтов распространена во многих драйверах принтера. Даже если вы выберете шрифт TrueType или OpenType, вы можете не получить такой же шрифт на принтере, как на экране.
Неквадратные пиксели распространены в некоторых принтерах. Это может испортить даже приличные растеризаторы, когда вы делаете что-то, что не выровнено по оси.
Кернинг может быть удивительным. Не предполагайте, что ширина ('a') + ширина ('b') == ширина ("ab").
Ошибки округления легко сделать. Иногда вам нужно знать правила округления, используемые базовым растеризатором.
Измерение в неправильном контексте слишком распространено. Не пытайтесь измерять в контексте экрана и применять преобразование, чтобы добраться до блоков принтера. Если вам нужны единицы принтера, измерьте в контексте принтера.
Сегодня я придерживаюсь мнения, что если вам нужен только предварительный просмотр печати, то вы должны разместить в единицах принтера растровое изображение, размер которого соответствует размеру страницы, и масштабировать растровое изображение до экрана на основе соотношений точек на дюйм. Это проще всего сделать и дает хорошие результаты. Но не ясно, действительно ли вам нужна хорошая распечатка и предварительный просмотр.
У меня были схожие требования и я столкнулся с теми же проблемами с отображением текста и масштабированием. Даже мои попытки с WPF (.NET 3.5) были кошмаром по многим причинам.
Я закончил с использованием GDI+ graphics.DrawString
несмотря на то, что он "устарел", с забавным трюком для получения точных измерений текста.
static public RectangleF MeasureInkBox(Graphics graphics, string text, Font font)
{
var bounds = new RectangleF();
using (var textPath = new GraphicsPath())
{
textPath.AddString(
text,
font.FontFamily,
(int)font.Style,
font.Size,
new PointF(0, 0),
StringFormat.GenericTypographic );
bounds = textPath.GetBounds();
}
return bounds;
}
Скорость и качество оказались удовлетворительными. Также, graphics.DrawString
рекомендуемый способ печати текста с помощью winforms.
Измерить отдельные позиции символов может быть сложно из-за кернинга, но я думаю, что немного более сложная версия этого метода может сделать эту работу. Например, для "Hello" он будет измерять "H", затем "He", затем Hel"и т. Д. Он не должен значительно ухудшать характеристики, особенно если вы делаете это на лету, когда получаете щелчок по слову.