При формировании печатных форм (ценников, этикеток, актов, накладных или договоров) разработчики часто сталкиваются с проблемой: текст не помещается в границы отведенной ячейки. В результате важная информация обрезается, переносится на следующую строку, ломая верстку всей страницы, или просто выглядит неэстетично. Стандартное решение — динамическое масштабирование шрифта в зависимости от длины выводимой строки. Если фраза короткая, мы можем сделать шрифт крупнее для лучшей читаемости. Если длинная — уменьшить его до тех пор, пока он гарантированно не впишется в отведенные рамки ячейки без переносов.
В этой статье мы подробно проанализируем ситуацию, выясним причины технических ограничений платформы 1С и разберем по шагам четыре надежных практических метода решения этой задачи.
СтрДлина не решает задачу?Первое интуитивное решение, которое приходит в голову начинающим разработчикам, — посчитать количество символов в строке с помощью функции СтрДлина и пропорционально уменьшить размер шрифта. Однако этот метод совершенно нежизнеспособен на практике по следующим причинам:
Для качественного решения нам нужны методы, которые анализируют физический размер текста в пикселях или миллиметрах. Рассмотрим подробнее каждый из возможных вариантов.
Это самый популярный, надежный и кроссплатформенный метод, который не требует использования внешних библиотек, COM-объектов или Windows API. Он работает на уровне платформы 1С и базируется на механизме автоматического расчета высоты строки при переносе текста.
Идея метода заключается в том, что мы создаем временный «невидимый» табличный документ в оперативной памяти, выводим туда нашу строку и заставляем платформу саму рассчитать, переносится ли текст на новую строку. Если переносится — значит, текст не влез по ширине, и размер шрифта нужно уменьшать.
Рассмотрим по шагам алгоритм реализации этого метода:
// Функция подбирает оптимальный размер шрифта для текста, чтобы он поместился в одну строку
Функция ПодобратьРазмерШрифта(Знач ИсходныйТекст, Знач ИмяШрифта, Знач ШиринаКолонки, Знач МаксРазмер = 14, Знач МинРазмер = 6) Экспорт
// 1. Создаем временный табличный документ в памяти
ВременныйТабДок = Новый ТабличныйДокумент;
// 2. Настраиваем ширину колонки, соответствующую ширине нашей ячейки в целевом документе
ВременныйТабДок.Области.Найти("C1").ШиринаКолонки = ШиринаКолонки;
// Получаем рабочую ячейку
ТестоваяЯчейка = ВременныйТабДок.Область("R1C1");
ТестоваяЯчейка.Текст = ИсходныйТекст;
// Включаем режим переноса текста и автовысоту строки
ТестоваяЯчейка.РазмещениеТекста = ТипРазмещенияТекстаТабличногоДокумента.Перенос;
ВременныйТабДок.Области.Найти("R1").АвтоВысотаСтроки = Истина;
// Также подготовим контрольную ячейку с гарантированно коротким текстом в одну строку
КонтрольнаяЯчейка = ВременныйТабДок.Область("R2C1");
КонтрольнаяЯчейка.Текст = "ВысотаОднойСтроки";
ВременныйТабДок.Области.Найти("R2").АвтоВысотаСтроки = Истина;
// 3. Цикл подбора размера шрифта (линейный поиск сверху вниз)
ТекущийРазмер = МаксРазмер;
Пока ТекущийРазмер >= МинРазмер Цикл
// Устанавливаем тестируемый шрифт для обеих ячеек
НовыйШрифт = Новый Шрифт(ИмяШрифта, ТекущийРазмер);
ТестоваяЯчейка.Шрифт = НовыйШрифт;
КонтрольнаяЯчейка.Шрифт = НовыйШрифт;
// Принудительно заставляем 1С пересчитать геометрию документа
ВысотаТест = ВременныйТабДок.Область("R1").ВысотаСтроки;
ВысотаКонтроль = ВременныйТабДок.Область("R2").ВысотаСтроки;
// Если высота тестовой ячейки равна высоте контрольной — текст поместился без переноса!
Если ВысотаТест <= ВысотаКонтроль И ВысотаТест > 0 Тогда
Возврат ТекущийРазмер;
КонецЕсли;
// Если текст не влез, уменьшаем размер шрифта на 1 пункт
ТекущийРазмер = ТекущийРазмер - 1;
КонецЦикла;
// Если даже при минимальном размере текст не влез, возвращаем минимально допустимый
Возврат МинРазмер;
КонецФункции
Оптимизация метода: Если диапазон возможных размеров шрифта велик (например, от 8 до 48 пунктов), последовательный перебор в цикле может работать медленно. В этом случае мы рекомендуем использовать алгоритм бинарного поиска (деления пополам). Это позволит сократить количество итераций проверки геометрии ячейки с десятков до 3-5 шагов.
Если вам нужно узнать абсолютно точную ширину текста в пикселях без манипуляций с табличными документами, можно использовать системный движок рендеринга веб-страниц через COM-объект HTMLFile. Этот метод работает молниеносно и очень точно определяет физические размеры строки.
Проанализируем пример кода, реализующий этот подход:
// Функция рассчитывает физическую ширину текста в пикселях
Функция ПолучитьШиринуТекстаВПикселях(Знач Текст, Знач СемействоШрифта, Знач РазмерШрифтаВПунктах, Знач Жирный = Ложь, Знач Курсив = Ложь) Экспорт
// Создаем невидимый HTML-документ в памяти
HTMLДокумент = Новый COMОбъект("HTMLFile");
HTMLДокумент.open();
// Формируем стили для точного соответствия шрифту 1С
СтильШрифта = "font-family: '" + СемействоШрифта + "'; font-size: " + Строка(РазмерШрифтаВПунктах) + "pt;";
Если Жирный Тогда
СтильШрифта = СтильШрифта + " font-weight: bold;";
КонецЕсли;
Если Курсив Тогда
СтильШрифта = СтильШрифта + " font-style: italic;";
КонецЕсли;
// Записываем HTML-код со скрытым контейнером span, который не переносит строки
HTMLКод = "<html><body>"
+ "<span id='text_container' style='" + СтильШрифта + " white-space: nowrap; position: absolute; visibility: hidden;'>"
+ Текст
+ "</span>"
+ "</body></html>";
HTMLДокумент.write(HTMLКод);
HTMLДокумент.close();
// Извлекаем элемент из DOM-дерева и получаем его физическую ширину
Контейнер = HTMLДокумент.getElementById("text_container");
ШиринаВПикселях = Контейнер.offsetWidth;
Возврат ШиринаВПикселях;
КонецФункции
Преимущество этого метода в том, что мы получаем точный физический размер. Зная ширину нашей колонки в табличном документе (которую можно перевести в пиксели по формуле, зависящей от настроек экрана), мы можем математически рассчитать требуемый коэффициент сжатия или размер шрифта за одну итерацию, избегая долгих циклов.
Этот метод незаменим, когда критически важна скорость формирования печатных форм (например, при массовой печати десятков тысяч ценников или этикеток в цикле), где создание виртуальных документов или COM-объектов вызовет серьезные задержки.
Суть метода заключается в предварительном расчете коэффициентов относительной ширины для каждого символа алфавита относительно некоторого базового символа для выбранного шрифта (например, Arial). Разработчик один раз составляет соответствие, где указывает «вес» каждой буквы.
Посмотрим на пример реализации такого алгоритма:
// Функция быстро оценивает условную ширину строки на основе базы весов символов
Функция РассчитатьУсловнуюДлинуСтроки(Знач ВходнаяСтрока, Знач РазмерШрифта) Экспорт
// Создаем соответствие символов и их весов (значения подобраны экспериментально для Arial)
ТаблицаВесов = Новый Соответствие;
// Широкие символы
ТаблицаВесов.Вставить("Ш", 0.95);
ТаблицаВесов.Вставить("Ж", 0.95);
ТаблицаВесов.Вставить("М", 0.90);
ТаблицаВесов.Вставить("Ф", 1.00);
// Стандартные символы
ТаблицаВесов.Вставить("А", 0.70);
ТаблицаВесов.Вставить("Б", 0.65);
ТаблицаВесов.Вставить("а", 0.55);
ТаблицаВесов.Вставить("о", 0.55);
// Узкие символы
ТаблицаВесов.Вставить("і", 0.22);
ТаблицаВесов.Вставить("л", 0.50);
ТаблицаВесов.Вставить("т", 0.45);
ТаблицаВесов.Вставить(" ", 0.28); // пробел
СреднийВес = 0.60; // значение для символов, не описанных в таблице
СуммарныйВес = 0;
Длина = СтрДлина(ВходнаяСтрока);
Для Индекс = 1 По Длина Цикл
ТекущийСимвол = Сред(ВходнаяСтрока, Индекс, 1);
ВесСимвола = ТаблицаВесов.Получить(ТекущийСимвол);
Если ВесСимвола = Неопределено Тогда
ВесСимвола = СреднийВес;
КонецЕсли;
СуммарныйВес = СуммарныйВес + ВесСимвола;
КонецЦикла;
// Рассчитываем итоговый габарит с учетом кегля шрифта
Возврат СуммарныйВес * (РазмерШрифта / 10); // Относительно базового размера 10pt
КонецФункции
Несмотря на то, что метод является приблизительным, он работает в сотни раз быстрее любого другого программного способа. Путем несложной калибровки весов под ваш корпоративный шрифт вы сможете добиться высокой точности попадания в границы ячеек.
Для разработчиков, стремящихся к абсолютной точности (например, при верстке сложных графических документов под термопринтеры этикеток с жесткими требованиями к границам печати), оптимальным решением станет использование внешних компонент (Native API), написанных на C++ или C# (.NET).
Такие компоненты напрямую вызывают функции операционной системы, предназначенные для работы с графикой (Graphics Device Interface):
GetTextExtentPoint32 вычисляет ширину и высоту заданной строки текста для указанного контекста устройства (DC) и выбранного шрифта.Graphics.MeasureString позволяет получить точные размеры рамки текста с учетом всех нюансов сглаживания и межсимвольных интервалов.Этот способ требует регистрации внешней компоненты на стороне клиента или сервера 1С, однако он гарантирует стопроцентное совпадение экранных размеров с результатами физической печати. Для визуальной проверки и замера реальных размеров макета перед выводом на печать можно использовать экранную линейку для подгонки размеров печатных форм в 1С.
В процессе разработки динамического масштабирования обязательно учитывайте следующие важные факторы:
АвтоМасштаб = Истина или задан фиксированный масштаб в процентах, это скорректирует физические размеры всех шрифтов при печати. Наш первый способ (с виртуальной таблицей) автоматически учитывает эти параметры, так как замеры происходят внутри самого ядра платформы 1С.