Существует ли простой способ заставить Windows вычислять экстенты текста с использованием фиксированного значения DPI вместо текущего значения DPI? - PullRequest
2 голосов
/ 16 октября 2010

Мне интересно, есть ли простой способ вычислить текстовый экстент строки (аналогично GetTextExtentPoint32 ), но позволяет мне указать DPI для использования в расчете,Другими словами, есть ли функция, которая делает именно то, что делает GetTextExtentPoint32 , но позволяет мне передавать DPI в качестве параметра или способ «обмануть» GetTextExtentPoint32 в использовании DPIчто я могу уточнить?

Прежде чем вы спросите: «С какой стати вы хотите это сделать?», я постараюсь объяснить, но потерпите, причины этого запроса несколько связаны.

В конечном счете, это для пользовательского алгоритма переноса слов, который разбивает длинную строку на более мелкие блоки текста, которые должны аккуратно помещаться в Crystal Report со сложными требованиями к макету текста (он имитирует бумажную форму, используемую полицейскими дляподавать уголовные жалобы, поэтому за макет отвечает государство, а не мы, и оно должно почти точно соответствовать бумажной форме.

Crystal Reports не может правильно выложить этот текст без посторонней помощи (текст должен помещаться внутри небольшого прямоугольника на одной странице, за которым следуют «стандартные» страницы продолжения, если текст переполняет маленькийблок), поэтому я написал код, разбивающий текст на несколько «блоков», которые хранятся в базе данных отчетов и затем отображаются в отчете по отдельности.

С учетом требуемых измерений (в логических дюймах) и шрифтаинформация, код сначала подгоняет текст к требуемой ширине, вставляя разрывы строк, а затем разбивает его на блоки правильного размера на основе высоты текста.Алгоритм использует функции VB6 TextHeight и TextWidth для вычисления экстентов, который возвращает те же результаты, что и функция GetTextExtentPoint32 (я проверял).

Этот код прекрасно работает, когда для дисплея установлено значение 96 точек на дюйм, но он имеет разрешение 120 точек на дюйм: в некоторых строках больше слов, чем в 96 точек на дюйм.

Например, «Быстрая коричневая лиса перепрыгивает через ленивую собаку» может сломаться следующим образом:

При 96 DPI

Theбыстрая коричневая лиса перепрыгивает через
ленивую собаку

при 120 DPI

быстрая коричневая лиса перепрыгивает через
ленивую собаку

Этот текст затем разбивается на Crystal Reports, поскольку первая строка больше не помещается в соответствующее текстовое поле отчета, поэтому фактический вывод отчета выглядит следующим образом:

Быстрая коричневая лиса перепрыгивает через

ленивую собаку

Сначала я подумал, что смогу компенсировать это, масштабируя результаты TextHeight и TextWidth снизился на 25%, но, видимо, жизнь не так проста: кажется, что многочисленные ошибки округления появляются (и, возможно, другие факторы?), так что экстент текста любой данной строки никогда не будет точно 25% больше при 120 DPI по сравнению с 96 DPI.Я не ожидал, что он отлично масштабируется, но иногда он даже не близок (в одном тесте ширина при 120 DPI была только на 18% больше, чем при 96 DPI).

Этого не происходитк любому тексту в отчете, который обрабатывается непосредственно Crystal Report: похоже, он очень хорошо масштабирует все, чтобы отчет был точно такой же, с 96 DPI и 120 DPI (и даже 144 DPI).Даже при печати отчета текст печатается в точности так, как он отображается на экране (т. Е. Он действительно выглядит как WYSIWYG).

Учитывая все это, поскольку я знаю, что мой код работает на 96 DPI, я долженбыть в состоянии решить эту проблему, рассчитав все мои текстовые экстенты с разрешением 96 DPI, даже если в настоящее время Windows использует другой параметр DPI.

Другими словами, я хочу, чтобы результат моей функции FitTextToRect возвращал тот же вывод при любом значении DPI, заставляя вычислять экстенты текста с использованием 96 DPI.Это должно сработать, так как я преобразую экстенты обратно в дюймы, а затем сравниваю их с требуемой шириной и высотой в дюймах.Я думаю, именно так получилось, что 96 DPI дает более точные результаты, чем 120 DPI, при преобразовании туда-сюда между пикселями и дюймами.

Я пролил на Windows Функции шрифтов и текста, посмотрев, может ли моя собственная функция вычислить экстент текста для заданного DPI, просмотрев GetTextMetrics и другие функции, чтобы увидеть, насколько это легко или сложносделать.Если есть более простой способ сделать это, я хотел бы знать, прежде чем я начну создавать свои собственные версии существующих функций Windows API!

Ответы [ 2 ]

1 голос
/ 17 октября 2010

Я нашел гораздо более простое решение.Мне потребовалось некоторое время, чтобы убедить себя, что это действительно имеет смысл.Решение было настолько очевидным, что я чувствую себя почти глупо, размещая его здесь.

В конечном счете, я действительно хочу, чтобы моя функция FitTextToRect производила одинаковый текстовый макет при любой настройке DPI.Оказывается, чтобы это произошло, на самом деле проще измерить все в пикселях.Поскольку любая другая единица измерения по определению примет во внимание текущую настройку DPI, использование чего-либо, кроме пикселей, может отбросить все.

Тогда «хитрость» заключается в том, чтобы заставить все вычисления работать одинаковорезультат они бы имели при 96 DPI.Однако вместо того, чтобы сначала вычислять экстенты текста, а затем уменьшать их, что добавляет значительную ошибку к вычислениям, вы можете получить более точные результаты (т. Е. Результаты будут равны или почти равны при любом DPI), если вы временно масштабируете размер шрифта уменьшится до вычисления любых экстентов текста.Обратите внимание, что этот исходный размер шрифта по-прежнему используется в предварительном просмотре и в выводе на печать.

Это работает из-за того, что Windows внутренне измеряет размер шрифта в единицах устройства , что дляэкран, означает пикселей.«Размер шрифта» шрифта преобразуется в пиксели приложением, позволяющим выбрать шрифт с использованием текущей настройки DPI:

font_height_in_pixels = (point_size * current_dpi / 72)

То есть Windows никогда не имеет дело непосредственно с размером шрифта: это всегдаиметь дело с пикселями.В результате он также рассчитывает экстенты текста в пикселях.

Это означает, что вы можете рассчитать коэффициент масштабирования на основе текущего DPI и размера точки шрифта, который уменьшит шрифт до нового размера точкикоторый всегда имеет одинаковое количество пикселей в любом DPI (я использовал 96 в качестве моего «базового» DPI):

scaled_point_size = (point_size * 96 / current_dpi)

Эффективно заставляя шрифт соответствовать разному количеству пикселей в любом DPI,это гарантирует, что экстенты текста будут одинаковыми в любом DPI, и поэтому алгоритм будет размещать текст одинаково в любом DPI.

Единственное, что мне нужно было сделать, это убедиться, что высота и ширинапараметры, переданные в функцию, которые измеряются в дюймах, были правильно преобразованы в пиксели.Я не мог использовать существующую функцию преобразования дюймов в пиксели VB6, поскольку она учитывает текущий DPI (что приведет к противоречивым результатам, поскольку высота и ширина текста «нормализованы» до 96 точек на дюйм), поэтому вместо этого я просто умножилвысота и ширина на 96, что преобразует их в то, что их измерения пикселей будут при 96 DPI.

1 голос
/ 16 октября 2010

GetTextMetrics принимает DC.Он использует настройки DPI от этого DC (например, вы не можете использовать настройки экрана и ожидать, что данные будут выходить в формате, приемлемом для принтера).

Так что все, что вам нужно сделать, это предоставить DCправильный DPI. Я думаю, вы можете напрямую управлять DPI метафайла DC.

Метафайлы - это векторная графика, поэтому они даже не выглядят так, как будто имеют DPI.

Вы можете управлять DPI с помощью CreateDIBitmap, но тогда нет никакого способа получить соответствующий DC.Вы можете увидеть, изменится ли DPI, если вы выберете этот DIB в DC памяти (CreateCompatibleDC).

Или вы можете использовать GDI +, создать битовую карту с нужным DPI, использовать конструктор Graphics, который работает сИзображение, а затем эта Графика будет иметь правильный DPI, поэтому функции измерения текста GDI + будут использовать ваш DPI по выбору.

...