gifts2017

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

Опубликовал Александр Гладких (yku) в раздел Программирование - Практика программирования

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

 

Вступление:

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

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

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

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

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 ,ТабДок);    
    
    Если РазмерШрифта <> Неопределено Тогда
        
        Надпись = Макет.Рисунки.Надпись;
        Надпись.Шрифт = Новый Шрифт(Надпись.Шрифт, ,РазмерШрифта);        
        Надпись.Текст = Текст;        
        Макет.Показать();
        
    Иначе
        
        Предупреждение("Ошибка подбора размера (даже единственный символ не поместился)!");
        
    КонецЕсли;     
    
КонецПроцедуры
 

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

Скачать файлы

Наименование Файл Версия Размер
Печать ценников публикация подбор размера шрифта (Оптимизирован) 82
.epf 12,32Kb
19.04.12
82
.epf 12,32Kb Скачать
Наименование в несколько строк публикация 50
.epf 8,87Kb
05.04.12
50
.epf 8,87Kb Скачать
Печать ценников публикация подбор размера шрифта 31
.epf 12,28Kb
19.04.12
31
.epf 12,28Kb Скачать
Подбор размера шрифта в надписи табличного документа 20
.epf 8,82Kb
19.04.12
20
.epf 8,82Kb Скачать

См. также

Подписаться Добавить вознаграждение

Комментарии

1. Илья (i132) 06.04.12 10:36
Спасибо, за находку, +10$m
предлагаю поменять название на "подгонка размера шрифта под размеры ячейки". - смысл статьи как обходить случаи кода весь текст не помещается в ячейке.
2. Сергей Ожерельев (Поручик) 06.04.12 10:40
Кстати, да, задача редкая, но не тривиальная.
В прошлом году было подобное, но использовал другую методику, основанную на подгонке строки в ячейке и вычислении получившегося количества символов.

Неделю мозг ломал, пока не наткнулся
http://avb1c.narod.ru/rowheight.html

Но получилось достаточно корректно, по крайней мере для того набора строк высота строки вычислялась верно.
3. Сергей Ожерельев (Поручик) 06.04.12 10:42
4. Александр Гладких (yku) 06.04.12 13:41
(2) Да, по ссылке тоже интересное решение. Я б не додумался.
5. Александр Гладких (yku) 06.04.12 13:51
(1) Признаться честно, над названием статьи я, наверное, даже больше просидел, чем над самой статьей. Но с предложенным названием не согласен: статья у меня как раз не как обходить такие случаи, а как их выявить программно. С этим возникает сложность. А вот что делать при таких случаях - это уже у кого на что фантазии хватит.
6. Александр Гладких (yku) 06.04.12 14:00
(1) и спасибо за 10$m. Я и не знал что их можно перекидывать. Правда, я не знаю что с ними надо делать :)
7. Miffka (mikhailv) 09.04.12 15:26
Автору огромное спасибо за качественную и достаточно универсальную реализацию недостающей в 1С функции.
(2) Поручик, эта реализация мне понравилась больше, чем указанная вами.

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

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

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

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

// Скопируем область в новый табличный документ.
// И там уже будем играть с её свойствами.
ВремТабДок = Новый ТабличныйДокумент;
ВремТабДок.Вывести(пТабДок);
ВремОбласть = ВремТабДок.Область(пОбласть.Имя);

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

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

Если ВысотаДо >= ВысотаПосле Тогда
//Попробуем отловить умещение "на грани": добавим символ
ВремОбласть.Текст = пТекст + "Щ";
ВысотаПослеСимв = ВысотаОбластиВмм(ВремОбласть, ВремТабДок);
Если ВысотаДо >= ВысотаПослеСимв Тогда
Возврат(Истина);
Иначе
Возврат(Ложь);
КонецЕсли;

Иначе
Возврат(Ложь);
КонецЕсли;

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

Также на базе этой функции написал ПолучитьМассивУмещающихсяСтрок(пОбласть, пТабДок, пТекст), в которой текст разбивается на несколько умещающихся в указанную ячейку строк.
8. Сергей Ожерельев (Поручик) 09.04.12 15:34
(7) Я её и сам бы не стал использовать, если бы тогда появилась эта статья. Но времени на эксперименты особо не было.
9. Александр Гладких (yku) 09.04.12 17:08
(7) Пожалуйста. Надо будет подправить публикацию. А почему бы букву не добавить сразу, а не во второй итерации?
Вот так:

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

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

// Скопируем область в новый табличный документ.
// И там уже будем играть с её свойствами.
ВремТабДок = Новый ТабличныйДокумент;
ВремТабДок.Вывести(пТабДок);
ВремОбласть = ВремТабДок.Область(пОбласть.Имя);

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

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

Если ВысотаДо >= ВысотаПосле Тогда
Возврат(Истина);
Иначе
Возврат(Ложь);
КонецЕсли;

КонецФункции
10. Miffka (mikhailv) 09.04.12 17:15
(9) Согласен, ваш вариант лучше:)
11. Артур Балян (Ortos) 10.04.12 10:14
Хороший метод, для ячейки табличного документа самое оно!
12. lonelylockly (krylovim) 18.04.12 19:29
Хороший метод, спасибо автору!

Только есть одно узкое место - производительность:
у меня в печатной форме выводилось 9 строк с обязательным переносом, в случае не помещения текста в ячейку. Все это дело формировалось ~9 секунд - на мой взгляд слишком долго для небольшой печатной формы.
Замер производительности показал, что самым узким местом оказался вывод исходного табличного документа (пТабДок) во временный (ВремТабДок).
Вместо работы во временном табличном документе я предпочел сохранение/восстановление изменяемых свойств (АвтоВысотаСтроки, ВысотаСтроки и Текст) и работу оставил в исходном таб. документе.
Результат - 0,35 сек, т.е. практически в 30 раз быстрее.
13. Александр Гладких (yku) 18.04.12 23:56
(12) lonelylockly,
Спасибо за замечания. Да, я тоже заметил, что так медленней, но на моих задач замедление не было критичным и не выходило за рамки пары секунд.

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

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

КонецФункции
...Показать Скрыть
14. Александр Гладких (yku) 19.04.12 00:01
(12) Протестировал как со старым методом, так и с новым. В лидерах по медлительности "Высота = Надпись.Высота;". А вот сразу за ним как раз "ВремТабДок.Вывести(пТабДок);". Чуть-чуть отстает.
15. Александр Гладких (yku) 19.04.12 00:04
(12) Вот так флуд я устроил. Вот метод и быстро выполняющийся и поддерживающий возможность объединения ячеек (копия табличного документа создается вне цикла):
Функция ВысотаОбластиВмм(пОбласть, пТабДок)
	
	Надпись = пТабДок.Рисунки.Добавить(ТипРисункаТабличногоДокумента.Текст);	
	Надпись.Расположить(пОбласть);	
	Высота = Надпись.Высота;    
	пТабДок.Рисунки.Удалить(Надпись);
	
	Возврат(Высота);
	
КонецФункции

Процедура ВывестиТекстВОбласть(пОбласть, пТабДок, пТекст, пМаксимальныйРазмерТекста = 60)
	
	// Запомним высоту исходной ячейки.
	НачальнаяВысота = ВысотаОбластиВмм(пОбласть, пТабДок);	
	
	// Создадим копию области и изменим её свойства.
	
	ВремТабДок = Новый ТабличныйДокумент;
	ВремТабДок.Вывести(пТабДок);
	ВремОбласть = ВремТабДок.Область(пОбласть.Имя);	
	
	ВремОбласть.АвтоВысотаСтроки = Истина;
	ВремОбласть.ВысотаСтроки     = 0;
	
	ВремОбласть.Текст = пТекст + "Щ"; // +"Щ" - исключение ситуации "на грани переноса"
	
	// Подберем зармер шрифта
	
	РазмерТекста = пМаксимальныйРазмерТекста;
	ВремОбласть.Шрифт = Новый Шрифт(ВремОбласть.Шрифт, ,РазмерТекста);
	
	ТекВысота = ВысотаОбластиВмм(ВремОбласть, ВремТабДок);
	
	Пока ТекВысота > НачальнаяВысота И РазмерТекста > 0 Цикл
			
		РазмерТекста = РазмерТекста - 1;
		ВремОбласть.Шрифт = Новый Шрифт(ВремОбласть.Шрифт, ,РазмерТекста);
		ТекВысота = ВысотаОбластиВмм(ВремОбласть, ВремТабДок);
		
	КонецЦикла;		
	
	// Изменим размер шрифта исходной области и выведем текст.
	
	пОбласть.Шрифт = Новый Шрифт(пОбласть.Шрифт, ,РазмерТекста);
	пОбласть.Текст = пТекст;
	
КонецПроцедуры
...Показать Скрыть
16. Елена Елена (prog-eg) 19.04.12 12:45
(6) yku, за 10$m можно поднять публикацию
17. Артур Балян (Ortos) 25.04.12 08:57
Второй метод с элементом "Рисунок" подходит для любого шрифта, это плюс. Однако он дико медленный. Пробовал я тоже так делать сперва, отверг такой метод - для большого количества рисунков в которых много текста не подходит абсолютно.
18. Александр Гладких (yku) 25.04.12 11:52
(17) Ortos,
Попробовал на длинной строке... Можно состариться пока посчитает. Надо бы объединить два метода. Чтобы сначала анализировал длину строки, потом выбирал алгоритм поиска. Если строка длинная, то берет твой метод (надеюсь, можно на "ты").

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

А может, и полностью перейти на второй алгоритм. Вот только с работой завал.
19. lonelylockly (krylovim) 25.04.12 22:40
(14) yku, у меня такой разрыв скорее всего (~99%) связан с тем, что в области, которая копировалась во временный таб. док. находился рисунок(картинка). Не проверял, но практически уверен в этом. Так что грамотная организация именованных областей и формирование табличного документа окажутся гораздо более простым решением =)
20. Илья (i132) 27.04.12 15:00
По моим замерам в вашей обрабтке около 15 процентов уходит на создание/удаление временых ячеек и рисунков.
Если хранить ссылоку на временную ячейку -- ее размеры меняются при изменнии текста и шрифта.
Мой код:
ВремТабДок = Новый ТабличныйДокумент;
ВремТабДок.Вывести(ОбластьМакета);
ВремОбласть = ВремТабДок.Область("Комментарий");	
ВремОбласть.АвтоВысотаСтроки = Истина;
ИсходныйШрифт = ВремОбласть.Шрифт;

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

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

Работает в том числе и для ячеек заполняемых по параметру. Правда для случая объединных ячеек не проверял.
Первоначальные размеры шрифта, по моему лучше брать из исходной ячейки.
kimskiysanya; yku; kobets-meloman; +3 Ответить 4
21. Александр Гладких (yku) 27.04.12 16:32
(20) i132, Интересно. Действительно так быстрее. А можно еще и шрифт не подбирать, а после первого прогона его рассчитать
НовРазмерШрифта = Цел(РазмерШрифта / (Sqrt(ТекВысота / НачВысота)));

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

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

Но за алгоритм спасибо.
22. Николай Кобец (kobets-meloman) 15.08.12 05:40
(20)Что у Вас за область "Комментарий"? Это на макете та самая область, шрифт которой изменяется??? Возможно вместо названия области задать ее диапазон вида что-то вроде "R2C2:R2C7" ?
23. Илья (i132) 15.08.12 11:41
(22) kobets-meloman, да "Комментарий" это область на мекете размер шрифта которой можно менять, задать адрес в стиле "R2C2:R2C7" можно - но тогда надо следить за адресами при следующих доработках макета - проще сразу дать название области.
24. Николай Кобец (kobets-meloman) 20.08.12 05:33
(20) Еще такой вопрос. Кусок кода не приводит к зацикливанию? В этом цикле мне непонятно, где меняется "ПроверямНадпись.Высота"... Хотя нет, по логике зацикливания не будет. Цикл завершится, когда размер шрифта станет равным 1. А все-таки хочется, чтобы цикл завершался при условии "ПроверямНадпись.Высота>МаксВысота"
Пока ПроверямНадпись.Высота>МаксВысота Цикл
      флНадоменитьШрифт =Истина;
      ВремОбласть.Шрифт = Новый Шрифт(ВремОбласть.Шрифт,,ВремОбласть.Шрифт.Размер-1);
      Если ВремОбласть.Шрифт.Размер=1 Тогда
         Прервать;
      Конецесли;   
   КонецЦикла;
...Показать Скрыть


Извиняюсь за, наверное, глупый вопрос. Просто реально непонятно...
25. Александр Гладких (yku) 20.08.12 10:14
(24) kobets-meloman,
Еще такой вопрос. Кусок кода не приводит к зацикливанию?

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

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

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

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

PS. Немного смущают такие вопросы.
26. Maxim2012 (ZhokhovM) 06.11.12 16:01
Добрый день! Кто-нибудь проверял на управляемой форме? У меня почему текст полностью переносит на следующую строку вместо текущей...
27. Иван (SinglCOOLer) 16.12.12 12:15
на 1С:Предприятие 8.1 (8.1.15.14) отказывается работать, в этом месте Высота = Надпись.Высота всегда возвращается одна и та же высота :(
28. Александр Гладких (yku) 16.12.12 13:13
(27) SinglCOOLer,
Странно, в теории поведение должно быть одинаковое. Быть может, некорректно перенесли в 8.1? А так, для меня 8.1 утратила актуальность.
29. Иван (SinglCOOLer) 16.12.12 13:16
проверял этот же код (обработку сконвертил под 8.2) на 8.2 все срабатывает
30. Галина Ивлева (galinka1c8) 16.05.13 15:02
Автору большое спасибо, как раз пригодилось, задача хоть и редкая, но довольно занятная.
31. Александр Крынецкий (echo77) 09.09.13 08:47
Функция ТекстУмещаетсяВЯчейке() иногда тупит и упорно говорит что текст не умещается в ячейке, я подправил её следующим образом:

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

// Скопируем область в новый табличный документ.
// И там уже будем играть с её свойствами.
ВремТабДок = Новый ТабличныйДокумент;
ВремТабДок.Вывести(пТабДок);
ВремОбласть = ВремТабДок.Область(пОбласть.Имя);

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

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

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

Если ВысотаДо >= ВысотаПосле Тогда
Возврат(Истина);
Иначе
Возврат(Ложь);
КонецЕсли;

КонецФункции
32. Игорь Высоковских (via) 16.09.13 14:22
переделал в однопроцедурный вариант

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

...Показать Скрыть
yku; shumvlesu; supermax2000; +3 Ответить
33. Тимур Хасанов (fokas) 29.10.13 18:22
Не обязательно использовать элемент формы или .Показать() для изменения высоты ячейки под текст. Обнаружил, что переприсваивание например ширины любой из ячеек табличного документа действует так же. Т.е.:
ТабДок = Новый ТабличныйДокумент;
...
ТабДок.Область(1,1,1,1).ШиринаКолонки = ТабДок.Область(1,1,1,1).ШиринаКолонки; // вместо ТабДок.Показать()
...Показать Скрыть
yurii_host; yku; +2 Ответить 1
34. Александр Гладких (yku) 29.10.13 20:43
35. Константин Куликов (Светлый ум) 21.03.14 12:32
Мои пять копеек:

"Высота строки ТабДок - Программно"

ОбластьЯчейки = Макет.ПолучитьОбласть("ШиринаСтроки|Строка");
пНадпись  = ОбластьЯчейки.ТекущаяОбласть;
НачШирина = пНадпись.ШиринаКолонки;
Стр = СтрДлина(ВыборкаСтрок.Номенклатура);


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

36. Максим Кузнецов (Makushimo) 10.12.14 13:21
(20) i132,
вот тут

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


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

в ВремТабДок и ВремОбласть свойство Защита = ложь;
37. Vitaly Suhorukov (nghtmn_cmth) 01.02.16 11:54
Кто не хочет сильно заморачиваться:
1. Определяете сколько символов может войти в строку без переноса

СтрокаС = СтрЗаменить(СтрокаС,Символы.ПС, " ");
	Длинна =  СтрДлина(СтрокаС);
	КолСтрок =Окр( Длинна/Максимальное число символов+0.5,0,0);
	Позиция = 1;
	Для а=1 по КолСтрок Цикл
		ПодСтр = Сред(СтрокаС,Позиция,Максимальное число символов);
		Позиция= Позиция+Максимальное число символов;
		Сообщить(ПодСтр);
	КонецЦикла;
...Показать Скрыть
38. Александр Гладких (yku) 01.02.16 15:29
(37) nghtmn_cmth,
Проблема в том, что вот 10 символов "i"
iiiiiiiiii
а вот 10 символов "W"
WWWWWWWWWW
разница существенная.

А тем, кто не хочет "сильно заморачиваться", скорее всего эта статья вообще не нужна :)
39. Vitaly Suhorukov (nghtmn_cmth) 02.02.16 10:07
(38) yku, определяем сколько самых широких символов входит в строку и переносим, останется конечно часть строки пустой, но ведь мы не заморачиваемся
40. Сергей (Che) Коцюра (CheBurator) 02.02.16 22:09
я решил достаточно просто - для печати ценников подходит идеально, не зависит от размеров типов шрифтов.
http://infostart.ru/public/181223/

Аналогичный подход использую (с мелкой хитростью) для гарантированной печати "неотрывного" подвала.
41. Константин Исиченко (IsiKosta) 21.07.16 07:38
Возврат ВысотаДо >= ВысотаПосле;
42. Павел Баранов (Smilk) 23.09.16 11:51
ФункцияТекстУмещаетсяВЯчейке(пОбласть, пТабДок, пТекст )    
        
    ВысотаДо = ВысотаОбластиВмм(пОбласть, пТабДок);    
    
    ВремТабдок = Новый ТабличныйДокумент;
    
   	ВремТабдок .ПолеСверху                         = 0;
	ВремТабдок .ПолеСнизу                           = 0;
	ВремТабдок .РазмерКолонтитулаСверху = 0;
	ВремТабдок .РазмерКолонтитулаСнизу   = 0;
	ВремТабдок .РазмерСтраницы = "Custom";	
	ВремТабдок .ВысотаСтраницы = ВысотаДо ;
        
        пОбласть.Параметры.Значение = пТекст;

        Возврат	ВремТабдок .ПроверитьВывод(пОбласть);

КонецФункции
...Показать Скрыть

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


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


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