О пространственном мышлении (14.10.2022). |
2022 - Октябрь | |||
14.10.2022 17:49 | |||
Дано: - лист А4; - на листе всегда есть один рисунок и следующая за ним одна таблица с известным количеством строк. Они не должны вместе занимать больше одной страницы; - рисунок всегда вписан в холст (максимально возможную ширину и высоту для занимания изображением). Пропорции изображения при вставке в холст не должны изменяться; - размеры-формы холста и изображения хаотичны от страницы к странице - но фиксированы для конкретной страницы; - высота таблицы меняется от страницы к странице - но фиксирована для конкретной страницы; - размер шрифта может быть изменен в любой момент - но фиксирован для конкретной страницы и ее таблицы; - известны отступы при печати страницы на принтере. Найти: алгоритм вставки изображения в холст таким образом, чтобы его размер был максимален на странице при печати (разглядеть максимум деталей). Осложнения: тройная конвертация исходного кода страницы для принтера, ввиду забагованных ОС Astra Linux и среды программирования Qt v.5.3. Это выражается в: - самовольном преобразовании HTML-кода элементами textEdit и textBrowser (вставил в них одно - считал из них другое); - самовольном преобразовании преобразованного HTML-кода при сохранении в PDF; - самовольном преобразовании PDF-документа программой qpdfview (неправильное отображение относительно принтера); - глюки шрифтов. Например, использование шрифта "Verdana" порождает на распечатанном листе лишний целый пробел после буквы "ж" и лишние пол-пробела после буквы "л" - в полиячеечных таблицах. Осложнения 2, ввиду забагованных ОС Astra Linux и среды программирования Qt v.5.3: - работают не все теги стандартного HTML; - не работают CSS и слои. Часть с таблицей проста. Сохранять увеличиваемое количество ячеек таблицы в PDF до тех пор, пока количество страниц PDF-документа не изменится с 1 на 2. Зная количество ячеек на страницу и высоту страницы в пикселях (минус отступы в пикселях) - известно количество пикселей на строку. Значит, известна оставшаяся высота в пикселях для холста изображения. Ширина холста в пикселях определяется шириной страницы в пикселях, минус отступы, минус 4 пикселя (определяется экспериментально). А вот правильно вставить изображение в холст - это шибанешься. Проблема не в придумывании формул, а в проверке их правильности. Изображение может быть больше и меньше холста, иметь разные пропорции. И никто не отменял масштабирования изображения в холсте: например, изображение 23000x13000 пикселей - оптимально войдет в холст 230x130 пикселей. Придуманный данный короткий вариант - требовал проверки: const double dProportion_XY_Canvas = 1.0*iCanvas_X_Max/iCanvas_Y_Max; //Пропорция сторон холста. const double dProportion_XY_Picture = 1.0*iPicture_Width/iPicture_Height; //Пропорция сторон изображения. if (dProportion_XY_Picture >= dProportion_XY_Canvas) qsResult += " width=" + qsCanvas_X_Max; //Натянуть изображение на верхнюю грань холста - масштабирование по высоте произойдет автоматически, высота изображения не выйдет за пределы высоты холста. else qsResult += " height=" + qsCanvas_Y_Max; //Натянуть изображение на боковую грань холста - масштабирование по ширине произойдет автоматически, ширина изображения не выйдет за ширину холста. Ну а для проверки - нужно либо создать хрен знает сколько вариантов изображений и холстов в реальности, либо на бумажке все эти варианты начертить и примерить. Так как создание изображений - долго, а чертить на бумажке - ненадежно (нет пространственной ориентации), пришлось написать проверочный код на абсолютно все возможные варианты изображений и холста - и примерить его на исходный код с пропорциями. const QString qsCanvas_X_Max = "639"; //При печати: пикселей в миллиметре - 3.457шт. Соответствует ширине листа A4 210мм, минус края 24мм, минус 4 пикселя (почему они лишние - неясно). const QString qsCanvas_Y_Max = "300"; //При печати: чтобы таблица с максимально возможным количеством строк, идущая после рисунка, не уехала на другой лист. Т.о., на каждую зону - свой отдельный лист. //Нужен анализ, как правильно поместить изображение в холст - чтобы его отображение было максимально большим, не растянутым и не обрезанным. const int iCanvas_X_Max = qsCanvas_X_Max.toInt(); const int iCanvas_Y_Max = qsCanvas_Y_Max.toInt(); const double dProportion_XY_Canvas = 1.0*iCanvas_X_Max/iCanvas_Y_Max; //Пропорция сторон холста. const double dProportion_XY_Picture = 1.0*iPicture_Width/iPicture_Height; //Пропорция сторон изображения. qsResult += "<br><center><img src=\"" + kv_config->dirs().tmp + "/" + strData_SQL.strBZ[i].qsBZ_Number + "заменить_на_пустоту.png\""; //Чтобы формуляр изготавливался быстро: чем больше разрешение сохраняемого рисунка - тем больше тормозит сохранение в PDF - и, как следствие, функция создания разрыва страниц. //В Qt v.5.3 свойство "=auto" для width и height не работает. const double dProportion_X = 1.0*iPicture_Width/iCanvas_X_Max; //Пропорция ширин изображения и холста. const double dProportion_Y = 1.0*iPicture_Width/iCanvas_Y_Max; //Пропорция высот изображения и холста. if (dProportion_XY_Picture == 1) //Изображение - квадрат. { if (dProportion_XY_Canvas == 1) qsResult += " width=" + qsCanvas_X_Max + " height=" + qsCanvas_Y_Max; //Квадрат изображения - в квадрат холста. Изображение - полностью пропорционально холсту. Ширина и высота изображения - преобразуются в ширину и высоту холста. else if (dProportion_XY_Canvas < 1) qsResult += " width=" + qsCanvas_X_Max; //Квадрат изображения - в вертикальный прямоугольник холста. Разместить по верхней грани холста - с автоматическим уменьшением его height. else if (dProportion_XY_Canvas > 1) qsResult += " height=" + qsCanvas_Y_Max; //Квадрат изображения - в горизонтальный прямоугольник холста. Разместить по боковой грани холста - с автоматическим уменьшением его width. } else if (dProportion_XY_Picture > 1) //Изображение - горизонтальный прямоугольник. { if (dProportion_XY_Canvas == 1) qsResult += " width=" + qsCanvas_X_Max; //Горизонтальный прямоугольник изображения - в квадрат холста. Разместить по верхней грани холста - с автоматическим уменьшением его height. Можно и по боковой грани холста. else if (dProportion_XY_Canvas < 1) qsResult += " width=" + qsCanvas_X_Max; //Горизонтальный прямоугольник изображения - в вертикальный прямоугольник холста. Разместить по верхней грани холста - с автоматическим уменьшением его height. else if (dProportion_XY_Canvas > 1) //Горизонтальный прямоугольник изображения - в горизонтальный прямоугольник холста. Развилка на пропорции холста и изображения. { if (dProportion_XY_Picture == dProportion_XY_Canvas) qsResult += " width=" + qsCanvas_X_Max + " height=" + qsCanvas_Y_Max; //Изображение - полностью пропорционально холсту. Ширина и высота изображения - преобразуются в ширину и высоту холста. else if (dProportion_XY_Picture < dProportion_XY_Canvas) //Изображение - уже холста. Развилка на пропроцию высоты: не превысит ли высоту холста при размещении по верхней грани холста (высота изображения в холсте увеличится). { if (dProportion_Y == 1) qsResult += " width=" + qsCanvas_X_Max; //Высота изображения - идентична высоте холста. Разместить по верхней грани холста - с автоматическим уменьшением его height. else if (dProportion_Y < 1) qsResult += " width=" + qsCanvas_X_Max; //Высота изображения - войдет в высоту холста. Разместить по верхней грани холста - с автоматическим уменьшением его height. else if (dProportion_Y > 1) qsResult += " height=" + qsCanvas_Y_Max; //Высота изображения - не войдет в высоту холста. Разместить по боковой грани холста - с автоматическим уменьшением его width. } else if (dProportion_XY_Picture > dProportion_XY_Canvas) //Изображение - шире холста. Развилка на пропорцию ширины: не превысит ли ширину холста при размещении по боковой грани холста (ширина изображения в холсте увеличится). { if (dProportion_X == 1) qsResult += " height=" + qsCanvas_Y_Max; //Ширина изображения - идентична ширине холста. Разместить по боковой грани холста - с автоматическим уменьшением его width. else if (dProportion_X < 1) qsResult += " height=" + qsCanvas_Y_Max; //Ширина изображения - войдет в ширину холста. Разместить по боковой грани холста - с автоматическим уменьшением его width. else if (dProportion_X > 1) qsResult += " width=" + qsCanvas_X_Max; //Ширина изображения - не войдет в ширину холста. Разместить по верхней грани холста - с автоматическим уменьшением его height. } } } else if (dProportion_XY_Picture < 1) //Изображение - вертикальный прямоугольник. { if (dProportion_XY_Canvas == 1) qsResult += " height=" + qsCanvas_Y_Max; //Вертикальный прямоугольник изображения - в квадрат холста. Разместить по боковой грани холста - с автоматическим уменьшением его width. Можно и по верхней грани холста. else if (dProportion_XY_Canvas > 1) qsResult += " height=" + qsCanvas_Y_Max; //Вертикальный прямоугольник изображения - в горизонтальный прямоугольник холста. Разместить по боковой грани холста - с автоматическим уменьшением его width. else if (dProportion_XY_Canvas < 1) //Вертикальный прямоугольник изображения - в вертикальный прямоугольник холста. Развилка на пропорции холста и изображения. { if (dProportion_XY_Picture == dProportion_XY_Canvas) qsResult += " width=" + qsCanvas_X_Max + " height=" + qsCanvas_Y_Max; //Изображение - полностью пропорционально холсту. Ширина и высота изображения - преобразуются в ширину и высоту холста. else if (dProportion_XY_Picture < dProportion_XY_Canvas) //Изображение - уже холста. Развилка на пропроцию ширины: не превысит ли ширину холста при размещении по боковой грани холста (ширина изображения в холсте увеличится). { if (dProportion_X == 1) qsResult += " height=" + qsCanvas_Y_Max; //Ширина изображения - идентична ширине холста. Разместить по боковой грани холста - с автоматическим уменьшением его width. else if (dProportion_X < 1) qsResult += " height=" + qsCanvas_Y_Max; //Ширина изображения - войдет в ширину холста. Разместить по боковой грани холста - с автоматическим уменьшением его width. else if (dProportion_X > 1) qsResult += " width=" + qsCanvas_X_Max; //Ширина изображения - не войдет в ширину холста. Разместить по верхней грани холста - с автоматическим уменьшением его height. } else if (dProportion_XY_Picture > dProportion_XY_Canvas) //Изображение - шире холста. Развилка на пропорцию высоты: не превысит ли высоту холста при размещении по верхней грани холста (высота изображения в холсте увеличится). { if (dProportion_X == 1) qsResult += " width=" + qsCanvas_X_Max; //Высота изображения - идентична высоте холста. Разместить по верхней грани холста - с автоматическим уменьшением его height. else if (dProportion_X < 1) qsResult += " width=" + qsCanvas_X_Max; //Высота изображения - войдет в высоту холста. Разместить по верхней грани холста - с автоматическим уменьшением его height. else if (dProportion_X > 1) qsResult += " height=" + qsCanvas_Y_Max; //Высота изображения - не войдет в высоту холста. Разместить по боковой грани холста - с автоматическим уменьшением его width. } } } В итоге, задумка с пропорциями оказалась верной (и, опять же, потому что я так думаю, - и мозг может конкретно так обманывать). Но потом выяснилось, что она верна только потому, что повезло с автомасштабированием рисунка (холст из исходных данных прекращает свое существование, убиваясь интерполяцией). Если же оставить холст в таком виде, в котором он есть, - требуется совмещать центры изображения и рисунка - и плясать именно от них. А вот какие там корректные формулы для такого видения вопроса - XYZ знает. Возможно, поможет тупое перемещение рисунка на половину величины грани, на которую не натягивалось изображение, - минус половина размера изображения. (добавлено 28.11.2022) А алгоритм-то неверный! Нужно сделать дополнительную пропорцию при формировании итогового размера изображения в холсте. Иначе, если изображения нет, - подставляется какой-то левый значок-рисунок (судя по всему, показывающий, что изображения нет). И у этого рисунка свои размеры - соответственно, если не указывать размер второй части изображения, - все, что после такого недорисунка - поползет. //Ускорение создания документа: создается HTML без рисунков, рисунки создаются отдельно и потом подставляются в документ. //При первом проходе рисунки создаются. При втором - подцепить габариты уже существующих изображений. if (!strData_SQL->bLoaded) bPicture_Done = bMake_Picture(i, true, &iPicture_Width, &iPicture_Height, false, false); else if (QFile::exists(kv_config->dirs().tmp + "/" + g_qsNumber + ".png")) { bPicture_Done = true; QImage qImage; qImage.load(kv_config->dirs().tmp + "/" + g_qsBZ_Number + ".png"); iPicture_Height = qImage.height(); iPicture_Width = qImage.width(); } if (bPicture_Done) { //Нужен анализ, какой размер холста создавать для TextEdit, чтобы <IMG> (даже пустой) занимал ровно столько пикселей, сколько надо: для быстрого и правильного разрыва страницы при формировании формуляра. const int iCanvas_X_Max = 639; //При печати: пикселей в миллиметре - 3.457шт. Соответствует ширине листа A4 210мм, минус края 24мм, минус 4 пикселя (почему они лишние - неясно). const int iCanvas_Y_Max = 365; //При печати: чтобы таблица с максимально 30 строками, идущая после рисунка, не уехала на другой лист. Т.о., на каждую сущность - свой отдельный лист. //Нужен анализ, как правильно поместить изображение в холст - чтобы его отображение было максимально большим, не растянутым и не обрезанным. const double dProportion_XY_Canvas = 1.0*iCanvas_X_Max/iCanvas_Y_Max; //Пропорция сторон холста. const double dProportion_XY_Picture = 1.0*iPicture_Width/iPicture_Height; //Пропорция сторон изображения. //Для <IMG> нужно указывать и высоту тоже. Иначе при пустом изображении в принтер помещается значок отсутствующего рисунка, размеры которого иные - высота искажается - текст съезжает - создание разрыва страницы работает неправильно. const double dProportion_XX = 1.0*iCanvas_X_Max/iPicture_Width; const double dProportion_YY = 1.0*iCanvas_Y_Max/iPicture_Height; qsResult += "<br><center><img src=\"" + kv_config->dirs().tmp + "/" + g_qsBZ_Number + "заменить_на_пустоту.png\""; //Чтобы формуляр изготавливался быстро: чем больше разрешение - тем больше тормозит сохранение в PDF - и, как следствие, функция создания разрыва страниц. if (dProportion_XY_Picture >= dProportion_XY_Canvas) qsResult += " width=" + QString::number(iCanvas_X_Max) + " height=" + QString::number(round(iPicture_Height*dProportion_XX)); else qsResult += " width=" + QString::number(round(iPicture_Width*dProportion_YY)) + " height=" + QString::number(iCanvas_Y_Max); qsResult += "></img></center><br>"; |
|||
Обновлено ( 28.11.2022 09:30 ) |