Как определить размер текста в табличном документе 1С и динамически изменить шрифт

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При формировании печатных форм (ценников, этикеток, актов, накладных или договоров) разработчики часто сталкиваются с проблемой: текст не помещается в границы отведенной ячейки. В результате важная информация обрезается, переносится на следующую строку, ломая верстку всей страницы, или просто выглядит неэстетично. Стандартное решение — динамическое масштабирование шрифта в зависимости от длины выводимой строки. Если фраза короткая, мы можем сделать шрифт крупнее для лучшей читаемости. Если длинная — уменьшить его до тех пор, пока он гарантированно не впишется в отведенные рамки ячейки без переносов.

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

Почему стандартная функция СтрДлина не решает задачу?

Первое интуитивное решение, которое приходит в голову начинающим разработчикам, — посчитать количество символов в строке с помощью функции СтрДлина и пропорционально уменьшить размер шрифта. Однако этот метод совершенно нежизнеспособен на практике по следующим причинам:

  1. Пропорциональные шрифты. Большинство стандартных шрифтов (например, Arial, Times New Roman, Calibri) являются пропорциональными. Это означает, что разные символы имеют разную физическую ширину. Например, буква «Ш» или «Ж» занимает на экране и бумаге почти в три раза больше места, чем буква «і», «л» или знак точки. Две строки с одинаковым количеством символов («ШШШШШ» и «IIIII») будут иметь абсолютно разную длину в миллиметрах.
  2. Различные начертания. Полужирный шрифт (Bold) или курсив (Italic) существенно меняют итоговые габариты текста даже при одинаковом кегле (размере в пунктах).
  3. Специфика рендеринга. Платформа 1С использует системные библиотеки операционной системы для отрисовки шрифтов. Масштабирование зависит от разрешения экрана (DPI), настроек масштабирования ОС и конкретного драйвера принтера, отправляющего документ на печать.

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

Способ 1. Метод виртуального табличного документа (Чистый 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 шагов.

Способ 2. Измерение ширины через HTML-документ (Windows-клиенты)

Если вам нужно узнать абсолютно точную ширину текста в пикселях без манипуляций с табличными документами, можно использовать системный движок рендеринга веб-страниц через 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;
    
    Возврат ШиринаВПикселях;

КонецФункции

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

Способ 3. Математический метод на основе пропорциональных весов символов

Этот метод незаменим, когда критически важна скорость формирования печатных форм (например, при массовой печати десятков тысяч ценников или этикеток в цикле), где создание виртуальных документов или 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

КонецФункции

Несмотря на то, что метод является приблизительным, он работает в сотни раз быстрее любого другого программного способа. Путем несложной калибровки весов под ваш корпоративный шрифт вы сможете добиться высокой точности попадания в границы ячеек.

Способ 4. Внешние компоненты (WinAPI / GDI)

Для разработчиков, стремящихся к абсолютной точности (например, при верстке сложных графических документов под термопринтеры этикеток с жесткими требованиями к границам печати), оптимальным решением станет использование внешних компонент (Native API), написанных на C++ или C# (.NET).

Такие компоненты напрямую вызывают функции операционной системы, предназначенные для работы с графикой (Graphics Device Interface):

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

Важные нюансы и рекомендации при печати

В процессе разработки динамического масштабирования обязательно учитывайте следующие важные факторы:

  1. Влияние драйвера принтера. Поведение шрифтов при предварительном просмотре в 1С и при прямой отправке на конкретный принтер может различаться. Драйверы принтера накладывают свои ограничения на минимальные и максимальные размеры системных шрифтов. Всегда тестируйте результаты на реальном оборудовании.
  2. Масштаб самого табличного документа. Если у вашего табличного документа установлено свойство АвтоМасштаб = Истина или задан фиксированный масштаб в процентах, это скорректирует физические размеры всех шрифтов при печати. Наш первый способ (с виртуальной таблицей) автоматически учитывает эти параметры, так как замеры происходят внутри самого ядра платформы 1С.
  3. Минимальный порог читаемости. Никогда не уменьшайте шрифт до бесконечности. Если пользователю выведется строка длиной в 100 символов, сжатая до 3-го размера шрифта, прочитать её без лупы будет невозможно. Всегда задавайте разумный нижний лимит (например, 6 или 7 пунктов), ниже которого текст должен обрезаться с добавлением троеточия либо переноситься на новую строку.
← На главную