ilyachalov (ilyachalov) wrote,
ilyachalov
ilyachalov

Category:

Windows GDI, C++: выравнивание текста при печати

Этот пост продолжает тему предыдущего поста: «Windows GDI, C++: печать текста».

Функция DrawText из набора Windows API, которую я разобрал в предыдущем посте, может выравнивать текст влево, вправо или по центру. Эту возможность можно включить, добавив один из флагов DT_LEFT (по умолчанию), DT_RIGHT или DT_CENTER в пятый аргумент функции. (Функция DrawText может выполнять и вертикальное выравнивание, но в этом посте речь про горизонтальное.)

Недостаток функции DrawText в том, что она не умеет выполнять выравнивание текста по ширине (еще его называют «выравниванием текста по обеим сторонам»). Например, в HTML атрибут align тега <p> может принимать значения left (влево), right (вправо), center (по центру), justify (по обеим сторонам). В CSS свойство text-align может принимать те же значения (и еще некоторые другие, но сейчас речь не о них).

Кстати, эту тему сложно гуглить, потому что слово «justify» в англоговорящей среде имеет в первую очередь значение «оправдывать, объяснять, обосновывать, подтверждать, мотивировать», а значение «выравнивать по обеим сторонам» у этого слова появилось не так давно и используется только в специализированных темах, касающихся оформления текста. Например, «Яндекс.Переводчик» не знает об этом значении этого слова. Как и кембриджский словарь английских слов: justify.

А вот Google.Translate дает это значение третьим по порядку. Потому что в данном случае он использует базу оксфордского «Lexico»: justify.

Как вообще технически производится «выравнивание по обеим сторонам»? Цитата из вышеупомянутого словаря «Lexico»: «in most European languages you justify text by adding space between letters and words», то есть с помощью увеличения расстояния между буквами или словами. На Htmlbook.ru, кстати, пишут, что «чтобы произвести это действие браузер в этом случае добавляет пробелы между словами». Подозреваю, это неправильный перевод слова «space» на русский как «пробелы». Браузер не добавляет пробелы, а растягивает имеющиеся.

Для этого в наборе функций Windows GDI имеется функция SetTextJustification, однако, она влияет только на работу функций TextOut и ExtTextOut. Поэтому использование функции DrawText для реализации выравнивания текста по обеим сторонам отпадает.

Что же делать? В интернетах пишут, что функция DrawText построена на вызовах функции TextOut. В принципе, можно написать свою функцию DrawText (печать форматированного текста из множества строк), построив ее на вызовах функции TextOut (печать одной текстовой строки) и реализовав в этой новой функции требуемое выравнивание по обеим сторонам.

Я не буду писать полный аналог функции DrawText, но покажу первые шаги в этом направлении. Во-первых, требуется разбить заданный текст на строки, которые затем можно выводить функцией TextOut.

Заданный текст можно представить как одну очень длинную символьную строку. Переберем символы этой строки в цикле. Будем формировать другую строку, которая по ширине в пикселях должна влезть в максимальную ширину в пикселях блока печатаемого текста. В каждом витке цикла эта новая строка будет увеличиваться на один символ и мы будем при этом каждый раз измерять ширину этой новой строки в пикселях с помощью функции GetTextExtentPoint32. Максимальную ширину в пикселях блока печатаемого текста получим с помощью функции GetDeviceCaps (значение второго параметра должно быть равно HORZRES).

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

Текст программы: Output_to_printer_WinGDI_3_Justify1.cpp
Пример работы программы:


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

Исправим это. Как только формируемая строка выйдет за максимальную ширину в пикселях блока печатаемого текста, будем отступать назад на один символ, если это произошло в промежутке между словами. Если же это произошло в середине слова, отступим назад до ближайшего промежутка между словами.

Текст программы: Output_to_printer_WinGDI_3_Justify2.cpp
Пример работы программы:


Получилось то же самое, что и в предыдущем посте при использовании функции DrawText. То есть у нас получилось разбить заданный текст на множество строк, которые мы распечатали с помощью функции TextOut.

Теперь выполним выравнивание каждой отдельной строки, распечатанной функцией TextOut, по обеим сторонам. Для этого перед печатью каждой строки применим вышеупомянутую функцию SetTextJustification. Ее первый аргумент — контекст устройства (принтера в нашем случае), второй аргумент — расстояние в пикселях, на которое нужно увеличить ширину строки. Увеличение ширины строки выполняется за счет увеличения ширины «break characters» (символов, которые разделяют слова), по умолчанию эти символы — пробелы (но эти символы могут быть и не пробелами). Третий аргумент — количество символов, разделяющих слова (в нашем случае — пробелов). Количество пикселей, на которое будет увеличена ширина каждого пробела, функция рассчитает делением второго аргумента на третий.

Тут важно отметить, что после печати каждой отдельной строки необходимо будет обнулить установки, сделанные функцией SetTextJustification перед печатью строки. Функцию SetTextJustification для каждой строки необходимо применять к нерастянутой строке, а если результаты предыдущей SetTextJustification не обнулять, то каждое последующее применение функции SetTextJustification будет накладываться на расчеты, сделанные предыдущей функцией SetTextJustification. Тогда выравнивания не произойдет. Обнуление выполняется так:
SetTextJustification(pDC, 0, 0);

Текст программы: Output_to_printer_WinGDI_3_Justify3.cpp
Пример работы программы:


Видно, что получилось то, что и требовалось: текст выровнен и слева, и справа, то есть по обеим сторонам. Чтобы наглядно увидеть, как за счет увеличения ширины пробелов получается выравнивание каждой строки, можно сравнить эту картинку с предыдущей.
Tags: Английский язык, Образование, Программирование
Subscribe

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 2 comments