Всё началось с небольшого запроса: кнопка «Скачать PNG» в редакторе этикеток должна была открывать настоящий диалог Save As. После нажатия пользователь должен был увидеть понятное имя, выбрать место сохранения и получить файл, который можно опознать, не открывая.
Первая же версия имени выявила более глубокую проблему. Она использовала внутренний SKU вроде SKU-001, хотя на самой этикетке люди видели и использовали Batch No: 30267-0914. Скачивание работало, но семантика данных была неверной: система выбрала технически существующий, но бесполезный в работе идентификатор.
- Адрес в Production
- /tools/label/
- Бизнес-идентификатор в имени файла
- Видимый Batch No, если он задан
- Пример
- 30267-0914-BQ.png · 30267-0914-BQ.pdf
- Физический размер
- Без масштабирования; 80×80 мм остаются 80×80 мм
- Проверка пагинации
- Шесть размеров дали по одной странице в Chromium
Имя файла — часть модели данных
Правильным решением была не ещё одна функция именования специально для скачивания. Имя файла и видимое поле Batch No теперь берут значение из одного canonical-источника. Если на этикетке стоит 30267-0914, для PNG предлагается 30267-0914-BQ.png, а для PDF — 30267-0914-BQ.pdf. Если эти значения расходятся, как минимум одно из них уже не описывает партию, которую видит пользователь.
Для PDF действует тот же принцип, но интерфейс браузера устроен иначе. В macOS при переходе из браузерной печати к Print → Save as PDF предлагаемое имя PDF берётся из заголовка печатаемого документа. Поэтому перед печатью document.title ненадолго заменяется на имя из Batch No, а после завершения, отмены или возврата управления окну обычный заголовок надёжно восстанавливается. Это не смена SEO-заголовка, а ограниченное состояние с явными границами одного сеанса печати.
Одна страница превратилась в одну страницу и пустой лист
Когда имена стали согласованными, проявился более интересный дефект: этикетка 80×80 мм давала в предпросмотре две страницы, причём вторая была пустой.
Кажется очевидным, что лист 80×80 мм должен помещаться на странице 80×80 мм. Но движок браузера не сравнивает два целых значения в миллиметрах. CSS-миллиметры превращаются в CSS-пиксели, а печатный конвейер ещё раз переводит их в единицы устройства и пагинации. На границе страницы субпиксельное округление, стандартный строчный блок или небольшой вклад body могут вытолкнуть математически «точный» край в следующий pagination fragment.
Исходный print sheet имел размеры страницы, но всё ещё участвовал в обычной пагинации документа. Скрыть только #root было недостаточно: такой подход предполагал, что ни один другой дочерний элемент body не повлияет на печатную раскладку. Для точной границы страницы эта гарантия слишком слабая.
Исправляем пагинацию, а не размер этикетки
Правильное исправление изолирует print sheet от обычного потока пагинации. Печатные стили строго убирают все непечатные дочерние элементы body, обнуляют margin, padding и вклад строчного блока у html и body, а единственный печатаемый лист абсолютно позиционируют в начале страницы. Точное динамическое правило по текущей этикетке сохраняется:
@page { size: <width-mm> <height-mm>; margin: 0; }
Мы намеренно не использовали масштаб 99%, не вычитали по 1 мм из каждой стороны и не переносили этикетку на A4 с опцией «вписать в страницу». Такие приёмы могли бы убрать пустой лист, но одновременно нарушили бы главный контрак вывода. Этикетка 80×80 мм должна физически оставаться 80×80 мм.
Редактор и предпросмотр экспорта можно увеличить до 150%, чтобы с ними было удобнее работать, но это только способ показа. Canonical geometry и число пикселей не меняются: 100×100 мм по-прежнему экспортируются как 1000×1000 px, а 70×20 мм — как 700×200 px. Больший вид на экране не означает больший размер на бумаге.
Как отличить исправление пагинации от удачного исключения
Проверять только 80×80 мм было нельзя: специальный случай для одного размера легко создаёт убедительную, но хрупкую иллюзию. Проверка пагинации Chromium охватила 70×20, 70×40, 80×60, 80×80 и 100×100 мм, а также нестандартный размер 93×57 мм. Все шесть вариантов дали ровно одну страницу.
Автоматизированная среда не могла напрямую прочитать счётчик страниц в нативном предпросмотре печати macOS. Границу доказательств нужно формулировать точно: автоматика доказала, что печатные документы Chromium во всех шести размерах одностраничны; она не имитировала чтение недоступного значения из нативного UI.
Последний шаг произошёл за пределами браузера. После проверки print hotfix в Production содержимое проверенной ветки было сверено и возвращено в GitHub main, так что source tree и Production tree совпали. Это закрытие не позволит уже исправленной ошибке вернуться при следующем развёртывании из неполного исходного состояния.
«Скачивается» ещё не значит «готово», как и «печатается». Готово — это когда источник данных, имя файла, физические размеры, пагинация и исходники Production согласованы между собой.