Поместится ли текст в ячейке? (с примерами использования: авторазмер шрифта, перенос строк)




Периодически возникает вопрос: поместится ли текст в ячейке табличного документа? Хоть возникает он редко, но всё-таки возникает. В этой статье рассказывается как можно получить ответ на этот вопрос.

 

Вступление:

На просторах интернета я встречал различные решения, но все они носили «вероятностный» характер и строились на определении среднего размера символа (или максимального размера), вычислением длины строки, количеством строк, используя всяческие эмпирические коэффициенты, и всё равно не могли гарантировать, что текст поместится в ячейку.

Единственное решение, близкое к задаче, которое я нашел: Автоматический подбор размера шрифта в объекте РисунокТабличногоДокумента. Но оно для размещения текста в РисунокТабличногоДокумента, а не в область как в данной статье. Оно требует предварительной подготовки, но ничего проще именно для РисункаТабличногоДокумента на данный момент мне неизвестно (попытку упрощения см ниже).

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

Сам я эту проблему зачастую обходил теми или иными путями. И это всех удовлетворяло: и меня, и заказчиков. Видимо, из-за того, что встречается она редко:

1. В ценниках. Наименование товара обычно центрируют по высоте, и при очень длинном наименовании его начало и конец имеют риск не поместиться в отведенное ему место (а в ценниках обычно отключают автовысоту). Из этого положения два выхода:

а) Выбирать тот или иной шаблон ценника. Не реализовано в примерах.

б) Подгонять размер текста под ценник. Реализовано в примерах:

Авторазмер текста

 2. И когда наименование нужно написать на несколько строк:

В типовых это реализовано так:

Типовое решение вывода длинного текста

мне же больше по душе выводить его так (с автоматическим переносом слов):

человеческое написание

Решение:

Намучавшись за два вечера, пришел, надеюсь, к простому решению:

Идея такая:

1. Запоминаем исходный размер ячейки в мм.

2. Назначаем ячейке свойство АвтоВыстота = Истина

3. Вставляем текст и опять смотрим высоту ячейки.

4. Если высота ячейки изменилась в большую сторону — значит, текст не помещается.

Высоту ячейки в мм будем измерять с помощью вставки в ячейку Рисунка и измерением его размеров.

Подробно:

Функция получения высоты ячейки:

Функция ВысотаОбластиВмм(пОбласть, пТабДок)
    
    Надпись = пТабДок.Рисунки.Добавить(ТипРисункаТабличногоДокумента.Текст);    
    Надпись.Расположить(пОбласть);    
    Высота = Надпись.Высота;    
    пТабДок.Рисунки.Удалить(Надпись);
    
    Возврат(Высота);
    
КонецФункции

Функция для определения вместится ли текст в ячейку:

Функция ТекстУмещаетсяВЯчейке(пОбласть, пТабДок, пТекст)     
        
    ВысотаДо = ВысотаОбластиВмм(пОбласть, пТабДок);    
    
    // Скопируем область в новый табличный документ.
    //  И там уже будем играть с её свойствами.
    ВремТабДок = Новый ТабличныйДокумент;
    ВремТабДок.Вывести(пТабДок);
    ВремОбласть = ВремТабДок.Область(пОбласть.Имя);    
    
    ВремОбласть.АвтоВысотаСтроки = Истина;
    ВремОбласть.ВысотаСтроки     = 0;    
    
    ВремОбласть.Текст = пТекст;        
    ВысотаПосле = ВысотаОбластиВмм(ВремОбласть, ВремТабДок);
                
    Если ВысотаДо >= ВысотаПосле Тогда
        Возврат(Истина);
    Иначе
        Возврат(Ложь);
    КонецЕсли;

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

Для возможности использования этого метода необходимо соблюдать следующие условия:

1. Необходимо как-нибудь назвать область.

2. Заполнение области = Текст, а не Параметр или Шаблон. В принципе, можно использовать и то и другое, но код становиться мудрёней. Во всяком случае, я ничего простого не придумал. И так как жизнь это усложняет не сильно не стал заморачиваться из-за этого.

3. Размещение текста = Перенос. Вот это нужно обязательно! Размещение текста програмно поставить можно, но эффект от изменения, насколько я понял, появляется только после отображения табличного документа с этой ячейкой.

4. Область из одной строки. Т.е. объединять ячейки по вертикали нельзя (в тех которых должен проверяться текст). Исправлено в процессе публикации: создается копия табличного документа с областью и уже меняются её свойства, а не самого табличного документа.


 

В файлах вы найдете:

1. Пример с подбором размера шрифта с использованием функций из статьи.

2. Оптимизированный по быстродействию пример подбора размера шрифта с небольшой модификацией кода (но подбор размера шрифта — не оптимальный. Ниже есть некая попытка его вычилить а не уменьшать каждый раз на 1).

3. Вывод длинных строк в несколько ячеек.

4. Подбор размера шрифта в элементе «Рисунок табличного документа».


Рисунок табличного документа (Надпись):

Скажу сразу, на больших текстах, работает ужасно долго.

Так вот, с рисунком табличного документа (далее «надписью») пришлось повозиться. Всё дело в том, что при установленом свойстве «Авторазмер» сами размеры подгоняются под текст только после их отображения. Здесь вариантов два:

1. либо ждать какое время и затем смотреть размеры, например так: ПодключитьОбработчикОжидания(«ИзмеритьРазмеры», 0.1, Истина)

2. либо «заставить» эту заразу отображаться принудительно.

Первый — рабочий. Однако код с ним получается сложный. И на каждую иттерацию — минимум по 0.1 секунды. В итоге это очень долго. Но работает (вроверял на поле таблчного документа).

А вот второй. С помощью применения маленькой хитрости — всё шикарно работает. А хитрость в том, что есть у ТабличногоДокумента метод Показать(). Хорош он тем, что как раз после его исполнения, размеры надписи уже становятся реальными, подогнанными под размер текста. Но плох он тем, что окрывает окно, которое программно или не закроешь, или я просто не знаю как.

Думал, я думал. А точнее, экперементировал, я экспериментировал. И вот до чего дошел:

Создаем на форме элемент «ПолеТабличногоДокумента» выводим туда нашу надпись. И вуаля — вызов метода «ЭлементыФормы.ПолеТабличногоДокумента.Показать()» не открывает никаких дополнительных окон, да что там, пользователь вообще этого не заметит. Зато мы это заметим. Да и мало того, что заметим, так еще и поэксплуатируем в своих корыстных целях! Ведь после вызова этого метода все высоты и ширины устанавливаются в соответсвие с установленной настройкой «АвтоРазмер = Истина». Красота. Но самое замечательное, что полеТабличногоДокумента можно даже не отображать! Разве, что форму с этим самым полем надо обязательно открывать, что накладывает некоторые ограничения на использование данного метода. Но эту форму можно с прогрессбаром сделать, и ни у кого не будет вопросов насчет открытия каких-то лишних форм.

Код приводить в человеческий вид мне лень. Если что, то есть файл в примерах.

Хочу отметить пару интересных моментов:

1. Вычисление высоты надписи при её фиксированной ширине.

2. Попытка оптимизировать алгоритм поиска шрифта.


// ;сообщить(...) - отладка

Функция ВысотаНадписиПриФиксированнойШирине(пНадпись, пЭлТабДок, пТекст)
        
    СтруктураНачЗначений = Новый Структура("Ширина, Высота, Авторазмер, РазмещениеТекста");
    ЗаполнитьЗначенияСвойств(СтруктураНачЗначений, пНадпись);     
    
    //////////////////////////////////////////////////////////////////////////////
    
    НачШирина = пНадпись.Ширина;
    
    пНадпись.Авторазмер = Истина;
    пНадпись.РазмещениеТекста = ТипРазмещенияТекстаТабличногоДокумента.Обрезать;
        
    ТекТекст = "";
    ВсегоСимволов = СтрДлина(Текст);
    
    ИндексДлиныСтроки = 0;
    
    Для Индекс = 1 По ВсегоСимволов Цикл
        
        ИндексДлиныСтроки = ИндексДлиныСтроки + 1;        
        
        ТекСимвол = Сред(пТекст, Индекс, 1);        
        пНадпись.Текст = ТекТекст + ТекСимвол;
        
        пЭлТабДок.Показать();
        
        Если пНадпись.Ширина > НачШирина Тогда
            
            Если ИндексДлиныСтроки = 1 Тогда // не вместился единственный символ.
                Возврат(Неопределено);
            КонецЕсли;
            
            ТекТекст = ТекТекст + Символы.ПС;            
            Индекс = Индекс - 1; // да, да, я и сам не люблю такие манипуляции со счетчиком цикла, но исправлять лень.
            ИндексДлиныСтроки = 0;
            
        Иначе
            ТекТекст = ТекТекст + ТекСимвол;
        КонецЕсли;
        
    КонецЦикла;
    
    пНадпись.Текст = ТекТекст;
    пЭлТабДок.Показать();
    
    ИтоговаяВысота = пНадпись.Высота;
    
    //////////////////////////////////////////////////////////////////////////////
    
    ЗаполнитьЗначенияСвойств(пНадпись, СтруктураНачЗначений);
    
    Возврат(ИтоговаяВысота);
    
КонецФункции

Функция ПолучитьОптимальныйРазмерШрифта(пМакет, пИмяНадписи, пТекст, пМаксимальныйРазмерШрифта ,пТабДок)
    
    пТабДок.Вывести(пМакет);
    
    Надпись = пТабДок.Рисунки[пИмяНадписи];
    
    //////////////////////////////////////////////////////////////////////////
    // { Подгон размера шрифта
    
    НачВысота = Надпись.Высота;    
    
    РазмерШрифта = пМаксимальныйРазмерШрифта;
    
    Надпись.Шрифт = Новый Шрифт(Надпись.Шрифт, ,РазмерШрифта);
    ТекВысота = ВысотаНадписиПриФиксированнойШирине(Надпись, пТабДок, пТекст);
    
    Если ТекВысота = Неопределено Тогда
        Возврат(Неопределено);
    КонецЕсли;
    
    Пока НачВысота < ТекВысота
        И РазмерШрифта > 0 Цикл
        
        ;Сообщить("РазмерШрифта = "+РазмерШрифта);
                
        НовРазмерШрифта = Цел(РазмерШрифта / (Sqrt(ТекВысота / НачВысота)));        
        
        ;Сообщить("НовРазмерШрифта = "+НовРазмерШрифта);
        
        Если НовРазмерШрифта = РазмерШрифта Тогда НовРазмерШрифта = НовРазмерШрифта - 1 КонецЕсли;
        
        РазмерШрифта = НовРазмерШрифта;
        Надпись.Шрифт = Новый Шрифт(Надпись.Шрифт, ,РазмерШрифта);
        ТекВысота = ВысотаНадписиПриФиксированнойШирине(Надпись, пТабДок, пТекст);
        
    КонецЦикла;
    
    // Точная подгонка (в сторону увеличения)
    
    ИтоговыйРазмерШрифта = РазмерШрифта;
    
    Пока ТекВысота <> Неопределено
        И НачВысота > ТекВысота
        И РазмерШрифта < пМаксимальныйРазмерШрифта Цикл
        
        ИтоговыйРазмерШрифта = РазмерШрифта;
        
        ;Сообщить("РазмерШрифта = "+РазмерШрифта);
                
        НовРазмерШрифта = РазмерШрифта + 1;        
        
        ;Сообщить("НовРазмерШрифта = "+НовРазмерШрифта);
        
        РазмерШрифта = НовРазмерШрифта;
        Надпись.Шрифт = Новый Шрифт(Надпись.Шрифт, ,РазмерШрифта);
        ТекВысота = ВысотаНадписиПриФиксированнойШирине(Надпись, пТабДок, Текст);

    КонецЦикла;    
    
    // }
    ////////////////////////////////////////////////////////////////////

    //Если ТекВысота = Неопределено Тогда
    //    ИтоговыйРазмерШрифта = Неопределено;    
    //КонецЕсли;
    
    Возврат(ИтоговыйРазмерШрифта);    
    
КонецФункции

Процедура КнопкаВыполнитьНажатие(Кнопка)
    
    Макет = ПолучитьМакет("Макет");
    
    ТабДок = ЭлементыФормы.ПолеТабличногоДокумента;    
    ТабДок.Очистить(); // Мы же хотим посмотреть в удобном месте?    
    
    РазмерШрифта = ПолучитьОптимальныйРазмерШрифта(Макет, "Надпись", Текст, 80 ,ТабДок);    
    
    Если РазмерШрифта <> Неопределено Тогда
        
        Надпись = Макет.Рисунки.Надпись;
        Надпись.Шрифт = Новый Шрифт(Надпись.Шрифт, ,РазмерШрифта);        
        Надпись.Текст = Текст;        
        Макет.Показать();
        
    Иначе
        
        Предупреждение("Ошибка подбора размера (даже единственный символ не поместился)!");
        
    КонецЕсли;     
    
КонецПроцедуры
 

Удачных экспериментов!

52 Comments

  1. i132

    Спасибо, за находку, +10$m

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

    Reply
  2. Поручик

    Кстати, да, задача редкая, но не тривиальная.

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

    Неделю мозг ломал, пока не наткнулся

    http://avb1c.narod.ru/rowheight.html

    Но получилось достаточно корректно, по крайней мере для того набора строк высота строки вычислялась верно.

    Reply
  3. Поручик

    Делал тоже для 8.2.

    Reply
  4. yku

    (2) Да, по ссылке тоже интересное решение. Я б не додумался.

    Reply
  5. yku

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

    Reply
  6. yku

    (1) и спасибо за 10$m. Я и не знал что их можно перекидывать. Правда, я не знаю что с ними надо делать 🙂

    Reply
  7. mikhailv

    Автору огромное спасибо за качественную и достаточно универсальную реализацию недостающей в 1С функции.

    (2) Поручик, эта реализация мне понравилась больше, чем указанная вами.

    Пригодилось в печати спецификации на «разлинованной» таблице.

    Пока внедрял, наткнулся на то, что 1С (рел. 8.2.13.219) при крайних значениях заполнения в ячейке (когда текст чуть-чуть не умещается и переносится на другую строку) не правильно отрабатывает высоту (через картинку). При отладке видно, что текст ПЕРЕНЁССЯ, а высота картинки возвращается такой же, как и без переноса.

    Решил проблему дополнительной проверкой с добавлением одного символа:

    //Функция для определения вместится ли текст в ячейку:

    Функция ТекстУмещаетсяВЯчейке(пОбласть, пТабДок, пТекст)

    ВысотаДо = ВысотаОбластиВмм(пОбласть, пТабДок);

    // Скопируем область в новый табличный документ.

    // И там уже будем играть с её свойствами.

    ВремТабДок = Новый ТабличныйДокумент;

    ВремТабДок.Вывести(пТабДок);

    ВремОбласть = ВремТабДок.Область(пОбласть.Имя);

    ВремОбласть.АвтоВысотаСтроки = Истина;

    ВремОбласть.ВысотаСтроки = 0;

    ВремОбласть.Текст = пТекст;

    ВысотаПосле = ВысотаОбластиВмм(ВремОбласть, ВремТабДок);

    Если ВысотаДо >= ВысотаПосле Тогда

    //Попробуем отловить умещение «на грани»: добавим символ

    ВремОбласть.Текст = пТекст + «Щ»;

    ВысотаПослеСимв = ВысотаОбластиВмм(ВремОбласть, ВремТабДок);

    Если ВысотаДо >= ВысотаПослеСимв Тогда

    Возврат(Истина);

    Иначе

    Возврат(Ложь);

    КонецЕсли;


    Иначе

    Возврат(Ложь);

    КонецЕсли;

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

    Также на базе этой функции написал ПолучитьМассивУмещающихсяСтрок(пОбласть, пТабДок, пТекст), в которой текст разбивается на несколько умещающихся в указанную ячейку строк.

    Reply
  8. Поручик

    (7) Я её и сам бы не стал использовать, если бы тогда появилась эта статья. Но времени на эксперименты особо не было.

    Reply
  9. yku

    (7) Пожалуйста. Надо будет подправить публикацию. А почему бы букву не добавить сразу, а не во второй итерации?

    Вот так:

    //Функция для определения вместится ли текст в ячейку:

    Функция ТекстУмещаетсяВЯчейке(пОбласть, пТабДок, пТекст)

    ВысотаДо = ВысотаОбластиВмм(пОбласть, пТабДок);

    // Скопируем область в новый табличный документ.

    // И там уже будем играть с её свойствами.

    ВремТабДок = Новый ТабличныйДокумент;

    ВремТабДок.Вывести(пТабДок);

    ВремОбласть = ВремТабДок.Область(пОбласть.Имя);

    ВремОбласть.АвтоВысотаСтроки = Истина;

    ВремОбласть.ВысотаСтроки = 0;

    //ВремОбласть.Текст = пТекст;

    ВремОбласть.Текст = пТекст+»Щ»; // Для исключения ситуаций вида «на грани переноса»

    ВысотаПосле = ВысотаОбластиВмм(ВремОбласть, ВремТабДок);

    Если ВысотаДо >= ВысотаПосле Тогда

    Возврат(Истина);

    Иначе

    Возврат(Ложь);

    КонецЕсли;

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

    Reply
  10. mikhailv

    (9) Согласен, ваш вариант лучше:)

    Reply
  11. Ortos

    Хороший метод, для ячейки табличного документа самое оно!

    Reply
  12. krylovim

    Хороший метод, спасибо автору!

    Только есть одно узкое место — производительность:

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

    Замер производительности показал, что самым узким местом оказался вывод исходного табличного документа (пТабДок) во временный (ВремТабДок).

    Вместо работы во временном табличном документе я предпочел сохранение/восстановление изменяемых свойств (АвтоВысотаСтроки, ВысотаСтроки и Текст) и работу оставил в исходном таб. документе.

    Результат — 0,35 сек, т.е. практически в 30 раз быстрее.

    Reply
  13. yku

    (12) lonelylockly,

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

    Кстати, сначала я тоже непосредственно менял в самой области. Но заметил, что если «тестируемая» область содержит объединенную ячейку в несколько строк, то высота не восстанавливается. А как сделать чтобы она восстанавливалась, я не знаю. И я решил пожертвовать быстродействием и выиграть в универсальности.

    PS. И если бы скачали вротой файл («Наименование в несколько строк публикация»), то там как раз такой способ (менять его мне было лень):

    Функция ТекстУмещаетсяВЯчейке(пОбласть, пТабДок, пТекст)
    
    // Сохраним настройки области
    СтруктураЗначений = Новый Структура(«Текст, АвтовысотаСтроки, ВысотаСтроки»);
    ЗаполнитьЗначенияСвойств(СтруктураЗначений, пОбласть);
    
    ////////////////////////////////////////////////////////////­//////////////////
    
    пОбласть.Текст = «»;
    ВысотаДо = ВысотаОбластиВмм(пОбласть, пТабДок);
    
    пОбласть.АвтоВысотаСтроки = Истина;
    пОбласть.ВысотаСтроки     = 0;
    
    пОбласть.Текст = пТекст;
    ВысотаПосле = ВысотаОбластиВмм(пОбласть, пТабДок);
    
    ////////////////////////////////////////////////////////////­//////////////////
    
    // Восстановим настройки области
    ЗаполнитьЗначенияСвойств(побласть, СтруктураЗначений);
    
    Если ВысотаДо >= ВысотаПосле Тогда
    Возврат(Истина);
    Иначе
    Возврат(Ложь);
    КонецЕсли;
    
    КонецФункции

    Показать

    Reply
  14. yku

    (12) Протестировал как со старым методом, так и с новым. В лидерах по медлительности «Высота = Надпись.Высота;». А вот сразу за ним как раз «ВремТабДок.Вывести(пТабДок);». Чуть-чуть отстает.

    Reply
  15. yku

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

    Функция ВысотаОбластиВмм(пОбласть, пТабДок)
    
    Надпись = пТабДок.Рисунки.Добавить(ТипРисункаТабличногоДокумента.Текст);
    Надпись.Расположить(пОбласть);
    Высота = Надпись.Высота;
    пТабДок.Рисунки.Удалить(Надпись);
    
    Возврат(Высота);
    
    КонецФункции
    
    Процедура ВывестиТекстВОбласть(пОбласть, пТабДок, пТекст, пМаксимальныйРазмерТекста = 60)
    
    // Запомним высоту исходной ячейки.
    НачальнаяВысота = ВысотаОбластиВмм(пОбласть, пТабДок);
    
    // Создадим копию области и изменим её свойства.
    
    ВремТабДок = Новый ТабличныйДокумент;
    ВремТабДок.Вывести(пТабДок);
    ВремОбласть = ВремТабДок.Область(пОбласть.Имя);
    
    ВремОбласть.АвтоВысотаСтроки = Истина;
    ВремОбласть.ВысотаСтроки     = 0;
    
    ВремОбласть.Текст = пТекст + «Щ»; // +»Щ» — исключение ситуации «на грани переноса»
    
    // Подберем зармер шрифта
    
    РазмерТекста = пМаксимальныйРазмерТекста;
    ВремОбласть.Шрифт = Новый Шрифт(ВремОбласть.Шрифт, ,РазмерТекста);
    
    ТекВысота = ВысотаОбластиВмм(ВремОбласть, ВремТабДок);
    
    Пока ТекВысота > НачальнаяВысота И РазмерТекста > 0 Цикл
    
    РазмерТекста = РазмерТекста — 1;
    ВремОбласть.Шрифт = Новый Шрифт(ВремОбласть.Шрифт, ,РазмерТекста);
    ТекВысота = ВысотаОбластиВмм(ВремОбласть, ВремТабДок);
    
    КонецЦикла;
    
    // Изменим размер шрифта исходной области и выведем текст.
    
    пОбласть.Шрифт = Новый Шрифт(пОбласть.Шрифт, ,РазмерТекста);
    пОбласть.Текст = пТекст;
    
    КонецПроцедуры

    Показать

    Reply
  16. prog-eg

    (6) за 10$m можно поднять публикацию

    Reply
  17. Ortos

    Второй метод с элементом «Рисунок» подходит для любого шрифта, это плюс. Однако он дико медленный. Пробовал я тоже так делать сперва, отверг такой метод — для большого количества рисунков в которых много текста не подходит абсолютно.

    Reply
  18. yku

    (17) Ortos,

    Попробовал на длинной строке… Можно состариться пока посчитает. Надо бы объединить два метода. Чтобы сначала анализировал длину строки, потом выбирал алгоритм поиска. Если строка длинная, то берет твой метод (надеюсь, можно на «ты»).

    Только хочу модифицировать. Для каждого символа чтобы определял высоту и ширину этого символа и высоту строк. Затем высчитывал уже необходимый размер шрифта. Или не для каждого символа, а как у тебя, для буквы «Щ».

    А может, и полностью перейти на второй алгоритм. Вот только с работой завал.

    Reply
  19. krylovim

    (14) у меня такой разрыв скорее всего (~99%) связан с тем, что в области, которая копировалась во временный таб. док. находился рисунок(картинка). Не проверял, но практически уверен в этом. Так что грамотная организация именованных областей и формирование табличного документа окажутся гораздо более простым решением =)

    Reply
  20. i132

    По моим замерам в вашей обрабтке около 15 процентов уходит на создание/удаление временых ячеек и рисунков.

    Если хранить ссылоку на временную ячейку — ее размеры меняются при изменнии текста и шрифта.

    Мой код:

    ВремТабДок = Новый ТабличныйДокумент;
    ВремТабДок.Вывести(ОбластьМакета);
    ВремОбласть = ВремТабДок.Область(«Комментарий»);
    ВремОбласть.АвтоВысотаСтроки = Истина;
    ИсходныйШрифт = ВремОбласть.Шрифт;
    
    ПроверямНадпись = ВремТабДок.Рисунки.Добавить(ТипРисункаТабличногоДокумента.Текст);
    ПроверямНадпись.Расположить(ВремОбласть);
    
    МаксВысота = ПроверямНадпись.Высота;
    Пока Выборка.Следующий() Цикл
    ПараметрыМакета.Комментарий  = Выборка.Описание;
    ТабДокумент.Вывести(ОбластьМакета);
    
    флНадоменитьШрифт =ложь;
    ВремОбласть.ВысотаСтроки =0;
    ВремОбласть.тЕКСТ = Выборка.Описание;
    
    ВремОбласть.Шрифт = ИсходныйШрифт;
    Пока ПроверямНадпись.Высота>МаксВысота Цикл
    флНадоменитьШрифт =Истина;
    ВремОбласть.Шрифт = Новый Шрифт(ВремОбласть.Шрифт,,ВремОбласть.Шрифт.Размер-1);
    Если ВремОбласть.Шрифт.Размер=1 Тогда
    Прервать;
    Конецесли;
    КонецЦикла;
    Если флНадоменитьШрифт Тогда
    ПравимОбласть = ТабДокумент.Область(«Комментарий»);
    ПравимОбласть.Шрифт = ВремОбласть.Шрифт;
    КонецЕсли;
    КонецЦикла;
    

    Показать

    Работает в том числе и для ячеек заполняемых по параметру. Правда для случая объединных ячеек не проверял.

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

    Reply
  21. yku

    (20) i132, Интересно. Действительно так быстрее. А можно еще и шрифт не подбирать, а после первого прогона его рассчитать

    НовРазмерШрифта = Цел(РазмерШрифта / (Sqrt(ТекВысота / НачВысота)));

    Так рассчитывается, но не очень точно, может ошибиться на 1-3 кегля.

    Т.е. в первом проходе его рассчитали, а потом уже перебором можно уточнить.

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

    Но за алгоритм спасибо.

    Reply
  22. kobets-meloman

    (20)Что у Вас за область «Комментарий»? Это на макете та самая область, шрифт которой изменяется??? Возможно вместо названия области задать ее диапазон вида что-то вроде «R2C2:R2C7» ?

    Reply
  23. i132

    (22) kobets-meloman, да «Комментарий» это область на мекете размер шрифта которой можно менять, задать адрес в стиле «R2C2:R2C7» можно — но тогда надо следить за адресами при следующих доработках макета — проще сразу дать название области.

    Reply
  24. kobets-meloman

    (20) Еще такой вопрос. Кусок кода не приводит к зацикливанию? В этом цикле мне непонятно, где меняется «ПроверямНадпись.Высота»… Хотя нет, по логике зацикливания не будет. Цикл завершится, когда размер шрифта станет равным 1. А все-таки хочется, чтобы цикл завершался при условии «ПроверямНадпись.Высота>МаксВысота»

    Пока ПроверямНадпись.Высота>МаксВысота Цикл
    флНадоменитьШрифт =Истина;
    ВремОбласть.Шрифт = Новый Шрифт(ВремОбласть.Шрифт,,ВремОбласть.Шрифт.Размер-1);
    Если ВремОбласть.Шрифт.Размер=1 Тогда
    Прервать;
    Конецесли;
    КонецЦикла;

    Извиняюсь за, наверное, глупый вопрос. Просто реально непонятно…

    Reply
  25. yku

    (24) kobets-meloman,

    Еще такой вопрос. Кусок кода не приводит к зацикливанию?

    Нет.

    В этом цикле мне непонятно, где меняется «ПроверямНадпись.Высота»…

    Она напрямую не меняется.

    Область ограничена по ширине. В эту область вставлен текст. Нас интересует вариант когда текст из-за ограничений ширины выводится в несколько строк. Чем больше шрифт, тем больше строк. Меняем размер шрифта — 1С меняет размер области (подгоняет под текст), а мы смотрим её высоту. Как только высота области стала меньше, чем МаксВысота, размер шрифта стал максимальным, при котором текст вмещается в нашу область. Он то нам и нужен.

    Хотя нет, по логике зацикливания не будет. Цикл завершится, когда размер шрифта станет равным 1. А все-таки хочется, чтобы цикл завершался при условии «ПроверямНадпись.Высота>МаксВысота»

    Всем нам хочется того же. Что мы и получаем, разве нет? «Размер шрифта >= 1» — ограничение. Так как меньшие размеры шрифта в нашей задаче не интересены.

    PS. Немного смущают такие вопросы.

    Reply
  26. ZhokhovM

    Добрый день! Кто-нибудь проверял на управляемой форме? У меня почему текст полностью переносит на следующую строку вместо текущей…

    Reply
  27. SinglCOOLer

    на 1С:Предприятие 8.1 (8.1.15.14) отказывается работать, в этом месте Высота = Надпись.Высота всегда возвращается одна и та же высота 🙁

    Reply
  28. yku

    (27) SinglCOOLer,

    Странно, в теории поведение должно быть одинаковое. Быть может, некорректно перенесли в 8.1? А так, для меня 8.1 утратила актуальность.

    Reply
  29. SinglCOOLer

    проверял этот же код (обработку сконвертил под 8.2) на 8.2 все срабатывает

    Reply
  30. galinka1c8

    Автору большое спасибо, как раз пригодилось, задача хоть и редкая, но довольно занятная.

    Reply
  31. echo77

    Функция ТекстУмещаетсяВЯчейке() иногда тупит и упорно говорит что текст не умещается в ячейке, я подправил её следующим образом:

    Функция ТекстУмещаетсяВЯчейке(пОбласть, пТабДок, пТекст)

    // Скопируем область в новый табличный документ.

    // И там уже будем играть с её свойствами.

    ВремТабДок = Новый ТабличныйДокумент;

    ВремТабДок.Вывести(пТабДок);

    ВремОбласть = ВремТабДок.Область(пОбласть.Имя);

    ВысотаДо = ВысотаОбластиВмм(ВремОбласть, ВремТабДок);

    ВремОбласть.АвтоВысотаСтроки = Истина;

    ВремОбласть.ВысотаСтроки = 0;

    ВремОбласть.Текст = пТекст;

    ВысотаПосле = ВысотаОбластиВмм(ВремОбласть, ВремТабДок);

    Если ВысотаДо >= ВысотаПосле Тогда

    Возврат(Истина);

    Иначе

    Возврат(Ложь);

    КонецЕсли;

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

    Reply
  32. via

    переделал в однопроцедурный вариант

    Процедура ПоигратьсяСоШрифтамиОбласти(ТабДокумент,ИмяОбласти,знач МинимальныйШрифт=5,знач МаксимальныйШрифт=80) Экспорт
    ВремТабДокумент = Новый ТабличныйДокумент;
    ВремТабДокумент.Вывести(ТабДокумент.ПолучитьОбласть(ИмяОбласти));
    ВремОбласть = ВремТабДокумент.Область(ИмяОбласти);
    Надпись = ВремТабДокумент.Рисунки.Добавить(ТипРисункаТабличногоДокумента.Текст);
    Надпись.Расположить(ВремОбласть);
    МинимальныйШрифт = Мин(МинимальныйШрифт,ВремОбласть.Шрифт.Размер);
    МаксимальныйШрифт = Макс(МаксимальныйШрифт,ВремОбласть.Шрифт.Размер);
    ВысотаИсходная = Надпись.Высота;
    ВремОбласть.АвтоВысотаСтроки = Истина;
    ВремОбласть.ВысотаСтроки = 0;
    Высота = Надпись.Высота;
    Если ВысотаИсходная < Высота Тогда // Попробуем уменьшить
    Пока 1=1 Цикл
    Если Высота <= ВысотаИсходная Тогда
    ТабДокумент.Область(ИмяОбласти).Шрифт = Новый Шрифт(ВремОбласть.Шрифт,,ВремОбласть.Шрифт.Размер);
    Возврат;
    КонецЕсли;
    Если ВремОбласть.Шрифт.Размер <= МинимальныйШрифт Тогда
    ТабДокумент.Область(ИмяОбласти).Шрифт = Новый Шрифт(ВремОбласть.Шрифт,,МинимальныйШрифт);
    Возврат;
    КонецЕсли;
    ВремОбласть.Шрифт = Новый Шрифт(ВремОбласть.Шрифт,,ВремОбласть.Шрифт.Размер-1);
    Высота = Надпись.Высота;
    КонецЦикла;
    ИначеЕсли Высота < ВысотаИсходная Тогда // Попробуем увеличить
    Пока 1=1 Цикл
    Если ВысотаИсходная < Высота Тогда
    ТабДокумент.Область(ИмяОбласти).Шрифт = Новый Шрифт(ВремОбласть.Шрифт,,ВремОбласть.Шрифт.Размер-1);
    Возврат;
    КонецЕсли;
    Если МаксимальныйШрифт <= ВремОбласть.Шрифт.Размер Тогда
    ТабДокумент.Область(ИмяОбласти).Шрифт = Новый Шрифт(ВремОбласть.Шрифт,,МаксимальныйШрифт);
    Возврат;
    КонецЕсли;
    ВремОбласть.Шрифт = Новый Шрифт(ВремОбласть.Шрифт,,ВремОбласть.Шрифт.Размер+1);
    Высота = Надпись.Высота;
    КонецЦикла;
    КонецЕсли;
    КонецПроцедуры
    
    

    Показать

    Reply
  33. fokas

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

    ТабДок = Новый ТабличныйДокумент;
    …
    ТабДок.Область(1,1,1,1).ШиринаКолонки = ТабДок.Область(1,1,1,1).ШиринаКолонки; // вместо ТабДок.Показать()
    
    Reply
  34. yku

    (33) fokas, ну что, шикарно.

    Reply
  35. Светлый ум

    Мои пять копеек:

    «Высота строки ТабДок — Программно»

    ОбластьЯчейки = Макет.ПолучитьОбласть(«ШиринаСтроки|Строка»);
    пНадпись  = ОбластьЯчейки.ТекущаяОбласть;
    НачШирина = пНадпись.ШиринаКолонки;
    Стр = СтрДлина(ВыборкаСтрок.Номенклатура);
    
    
    КоэффициентВысотыСтроки      = Цел(Стр/НачШирина) + 1;
    ВысотаОднойСтрокиПоУмолчанию = Число(«12,5»);
    РасчетнаяВысотаСтроки        = ВысотаОднойСтрокиПоУмолчанию * КоэффициентВысотыСтроки;
    ТекущаяВысотаСтроки          = ОбластьМакета.ТекущаяОбласть.ВысотаСтроки;

    Показать

    Reply
  36. Makushimo

    (20) i132,

    вот тут

    ВремОбласть.Текст = пТекст

    ругается «поле недоступно для записи»

    в ВремТабДок и ВремОбласть свойство Защита = ложь;

    Reply
  37. nghtmn_cmth

    Кто не хочет сильно заморачиваться:

    1. Определяете сколько символов может войти в строку без переноса

    СтрокаС = СтрЗаменить(СтрокаС,Символы.ПС, » «);
    Длинна =  СтрДлина(СтрокаС);
    КолСтрок =Окр( Длинна/Максимальное число символов+0.5,0,0);
    Позиция = 1;
    Для а=1 по КолСтрок Цикл
    ПодСтр = Сред(СтрокаС,Позиция,Максимальное число символов);
    Позиция= Позиция+Максимальное число символов;
    Сообщить(ПодСтр);
    КонецЦикла;

    Показать

    Reply
  38. yku

    (37) nghtmn_cmth,

    Проблема в том, что вот 10 символов «i»

    iiiiiiiiii

    а вот 10 символов «W»

    WWWWWWWWWW

    разница существенная.

    А тем, кто не хочет «сильно заморачиваться», скорее всего эта статья вообще не нужна 🙂

    Reply
  39. nghtmn_cmth

    (38) определяем сколько самых широких символов входит в строку и переносим, останется конечно часть строки пустой, но ведь мы не заморачиваемся

    Reply
  40. CheBurator

    я решил достаточно просто — для печати ценников подходит идеально, не зависит от размеров типов шрифтов.

    http://infostart.ru/public/181223/

    Аналогичный подход использую (с мелкой хитростью) для гарантированной печати «неотрывного» подвала.

    Reply
  41. IsiKosta

    Возврат ВысотаДо >= ВысотаПосле;

    Reply
  42. Smilk
    ФункцияТекстУмещаетсяВЯчейке(пОбласть, пТабДок, пТекст )
    
    ВысотаДо = ВысотаОбластиВмм(пОбласть, пТабДок);
    
    ВремТабдок = Новый ТабличныйДокумент;
    
    ВремТабдок .ПолеСверху                         = 0;
    ВремТабдок .ПолеСнизу                           = 0;
    ВремТабдок .РазмерКолонтитулаСверху = 0;
    ВремТабдок .РазмерКолонтитулаСнизу   = 0;
    ВремТабдок .РазмерСтраницы = «Custom»;
    ВремТабдок .ВысотаСтраницы = ВысотаДо ;
    
    пОбласть.Параметры.Значение = пТекст;
    
    Возврат ВремТабдок .ПроверитьВывод(пОбласть);
    
    КонецФункции
    

    Показать

    Можно создать временный табличный документ, с высотой страницы = высотой ячейки в вашем макете. И проверять помещается ли текст на эту страницу. Ну и тут же можно сразу баловаться шрифтом в цикле,

    Пока НЕ ВремТабДок.ПроверитьВывод(пОбласть) Цикл
    пОбласть.ТекущаяОбласть.Шрифт = Новый Шрифт(пОбласть.ТекущаяОбласть.Шрифт, пОбласть.ТекущаяОбласть.Шрифт.Имя, пОбласть.ТекущаяОбласть.Шрифт.Размер — 1);
    КонецЦикла;

    // Или шаг уменьшения сделать больше, чтобы было меньше итераций. При таком подходе ВремТабдок создается 1 раз, а в цикле выполняется метод ПроверитьВывод(), так гораздо быстрее.

    Reply
  43. mark_oilbass

    Подскажите пожалуйста, как быть когда ячейка состоит из нескольких строк? Авто высота делает ячейку сильно большой из за того что она состоит из 11 строк.

    Reply
  44. mark_oilbass

    Решил пока вопрос так: получаю первую строку с первой по последнюю колонку R1C1:R11C62 потом объединяю так как строка разбивается на ячейки.

    ИмяОбласти = «R1C1:R1C62»;

    ОбластьСТоваром = ОбластьТаб.ПолучитьОбласть(ИмяОбласти);

    ОбластьСТоваром.Область(ИмяОбласти).Объединить();

    Reply
  45. vasiliy_09_05

    Доброго времени суток Дамы и Господа.

    Всех Дам с прошедшим праздником 8 Марта 🙂

    Товарищи, нужна Ваша помощь!

    Я понимаю, что публикация старая, но я взял ее за основу и у меня никак не выходит «Каменный цветок» 🙁

    Смысл озвучен в публикации -подобрать шрифт под ячейку.

    Но при попытке использования любого кода из публикации и комментариев программа возвращает мне не высоту строки по картинке, а среднюю погоду по России 🙁

    Подскажите пожалуйста, что я делаю не так.

    Код не выкладываю, та как он большой, выложил обработку

    Буду благодарен за любую помощь!!!

    Reply
  46. CheBurator

    есть такая штука: ПроверитьВывод() — есои у тебя получается 2 страницы — значит — сильно лишнее, уменьшая шрифт, проверяй повторно до тех пор пока не влезет на 1 стр. Пример на 77 — можно посмотреть здесь — и адаптировать его под 8-ку.. https://infostart.ru/public/181223/

    Reply
  47. vasiliy_09_05

    Доброго времени суток.

    Спасибо за ответ.

    Этот вариант уже предложил многоуважаемый Smilk в 42 посте, но у меня так же, даже если размер шрифта over 100 в приложенной мной обработке так же показывает, что этикетка поместиться на одном листе.

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

    Обработка данной публикации у меня работает без проблем, а в обработе, приложенной мной не хочет работать ни с одним вариантом, предложенном в этой публикации и комментариях 🙁

    Reply
  48. Merkalov

    Долго бился, давно бы бросил, если бы не знал, что померить гребанную строку всё же возможно.

    В итоге пришёл к такому варианту

    Функция ПолучитьВысотуСтроки(ТабДок, ОбластьТекста, ОбластьМакета, Текст)
    
    ОбластьМакета.Параметры.ПоСопрДокументам = Текст;
    
    ВремТабДок = Новый ТабличныйДокумент;
    ВремТабДок.Вывести(ТабДок);
    ВремТабДок.Вывести(ОбластьМакета);
    
    Область = ВремТабДок.Область(ОбластьТекста);
    Рисунок = ВремТабДок.Рисунки.Добавить(ТипРисункаТабличногоДокумента.Текст);
    Рисунок.Расположить(Область); // вписание рисунка в область, он сам подгоняет размеры под нее
    
    Возврат Рисунок.Высота;
    
    КонецФункции

    Показать

    ВысотаСтроки = ПолучитьВысотуСтроки(ТабДок, «R28C1:R28C1», ОбластьПоСопрДокПерваяСтрока, стрПоСопрДокументам); 

    //ОбластьТекста — это Область ячеек, конкретная, на макете, можно взять первую ячейке в строке высота которой вас интересует, я так и

    //сделал.

    //ОбластьМакета — это Область = Макет.ПолучитьОбласть(«ИмяОбласти»);

    //Текст — выводимый текст который нужно примерить.

    И получается, что если высота строки >4 ТОГДА СЛУЧИЛСЯ ПЕРЕНОС, у обычной строки в моём макете высота 3,9, в вашем макете -нужно просто посмотреть в отладчике.

    Если ВысотаСтроки > 4 Тогда

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

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

    Reply
  49. CheBurator

    а есть ли в 8-ке такая вещь типа

    на форме — надпись.

    текст в надписи может быть переменной длины в надписб максимально возможным шрифтом.

    Reply
  50. user1086697

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

    Reply
  51. Grivba
    Reply
  52. Grivba
    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *