Merge branch 'main' into fakeScantoScanEffect

This commit is contained in:
Anthony Stirling 2025-07-16 13:42:53 +01:00 committed by GitHub
commit 51b61077fe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 538 additions and 258 deletions

3
.gitignore vendored
View File

@ -128,6 +128,9 @@ SwaggerDoc.json
/app/core/build
/app/common/build
/app/proprietary/build
common/build
proprietary/build
stirling-pdf/build
# Byte-compiled / optimized / DLL files
__pycache__/

View File

@ -143,7 +143,7 @@ Stirling-PDF currently supports 40 languages!
| Portuguese (Português) (pt_PT) | ![70%](https://geps.dev/progress/70) |
| Portuguese Brazilian (Português) (pt_BR) | ![77%](https://geps.dev/progress/77) |
| Romanian (Română) (ro_RO) | ![59%](https://geps.dev/progress/59) |
| Russian (Русский) (ru_RU) | ![70%](https://geps.dev/progress/70) |
| Russian (Русский) (ru_RU) | ![81%](https://geps.dev/progress/81) |
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) | ![97%](https://geps.dev/progress/97) |
| Simplified Chinese (简体中文) (zh_CN) | ![95%](https://geps.dev/progress/95) |
| Slovakian (Slovensky) (sk_SK) | ![53%](https://geps.dev/progress/53) |

View File

@ -43,7 +43,11 @@ public class AutoJobAspect {
// This aspect will run before any audit aspects due to @Order(0)
// Extract parameters from the request and annotation
boolean async = Boolean.parseBoolean(request.getParameter("async"));
log.debug("AutoJobAspect: Processing {} {} with async={}", request.getMethod(), request.getRequestURI(), async);
log.debug(
"AutoJobAspect: Processing {} {} with async={}",
request.getMethod(),
request.getRequestURI(),
async);
long timeout = autoJobPostMapping.timeout();
int retryCount = autoJobPostMapping.retryCount();
boolean trackProgress = autoJobPostMapping.trackProgress();
@ -219,10 +223,9 @@ public class AutoJobAspect {
resourceWeight);
}
/**
* Processes arguments in-place to handle file resolution and async file persistence.
* This approach avoids type mismatch issues by modifying the original objects directly.
* Processes arguments in-place to handle file resolution and async file persistence. This
* approach avoids type mismatch issues by modifying the original objects directly.
*
* @param originalArgs The original arguments
* @param async Whether this is an async operation

View File

@ -30,8 +30,7 @@ public class JobResult {
private String error;
/** List of result files for jobs that produce files */
@JsonIgnore
private List<ResultFile> resultFiles;
@JsonIgnore private List<ResultFile> resultFiles;
/** Time when the job was created */
private LocalDateTime createdAt;

View File

@ -46,7 +46,7 @@ dependencies {
implementation 'commons-io:commons-io:2.19.0'
implementation "org.bouncycastle:bcprov-jdk18on:$bouncycastleVersion"
implementation "org.bouncycastle:bcpkix-jdk18on:$bouncycastleVersion"
implementation 'io.micrometer:micrometer-core:1.15.1'
implementation 'io.micrometer:micrometer-core:1.15.2'
implementation 'com.google.zxing:core:3.5.3'
implementation "org.commonmark:commonmark:$commonmarkVersion" // https://mvnrepository.com/artifact/org.commonmark/commonmark
implementation "org.commonmark:commonmark-ext-gfm-tables:$commonmarkVersion"

View File

@ -142,9 +142,9 @@ multiPdfPrompt=Выберите PDF-файлы (2+)
multiPdfDropPrompt=Выберите (или перетащите) все необходимые PDF-файлы
imgPrompt=Выберите изображение(я)
genericSubmit=Отправить
uploadLimit=Maximum file size:
uploadLimitExceededSingular=is too large. Maximum allowed size is
uploadLimitExceededPlural=are too large. Maximum allowed size is
uploadLimit=Максимальный размер файла:
uploadLimitExceededSingular=слишком большой. Максимально допустимый размер
uploadLimitExceededPlural=слишком большой. Максимально допустимый размер
processTimeWarning=Внимание: Данный процесс может занять до минуты в зависимости от размера файла
pageOrderPrompt=Пользовательский порядок страниц (Введите список номеров страниц через запятую или функции типа 2n+1):
pageSelectionPrompt=Выбор страниц (Введите список номеров страниц через запятую 1,5,6 или функции типа 2n+1):
@ -170,67 +170,67 @@ sizes.medium=Средний
sizes.large=Большой
sizes.x-large=Очень большой
error.pdfPassword=PDF-документ защищен паролем, и пароль не был предоставлен или был неверным
error.pdfCorrupted=PDF file appears to be corrupted or damaged. Please try using the 'Repair PDF' feature first to fix the file before proceeding with this operation.
error.pdfCorruptedMultiple=One or more PDF files appear to be corrupted or damaged. Please try using the 'Repair PDF' feature on each file first before attempting to merge them.
error.pdfCorruptedDuring=Error {0}: PDF file appears to be corrupted or damaged. Please try using the 'Repair PDF' feature first to fix the file before proceeding with this operation.
error.pdfCorrupted=Файл PDF, по-видимому, поврежден. Пожалуйста, попробуйте сначала воспользоваться функцией "Восстановить PDF", чтобы исправить файл, прежде чем приступать к этой операции.
error.pdfCorruptedMultiple=Один или несколько PDF-файлов, по-видимому, повреждены. Пожалуйста, попробуйте сначала использовать функцию "Восстановить PDF" для каждого файла, прежде чем пытаться объединить их.
error.pdfCorruptedDuring=Ошибка {0}: Файл PDF, по-видимому, поврежден. Пожалуйста, попробуйте сначала воспользоваться функцией "Восстановить PDF", чтобы исправить файл, прежде чем приступать к этой операции.
# Frontend corruption error messages
error.pdfInvalid=The PDF file "{0}" appears to be corrupted or has an invalid structure. Please try using the 'Repair PDF' feature to fix the file before proceeding.
error.tryRepair=Try using the Repair PDF feature to fix corrupted files.
error.pdfInvalid=Файл PDF "{0}", по-видимому, поврежден или имеет неправильную структуру. Пожалуйста, попробуйте использовать функцию "Восстановить PDF", чтобы исправить файл, прежде чем продолжить.
error.tryRepair=Попробуйте использовать функцию восстановления PDF для исправления поврежденных файлов.
# Additional error messages
error.pdfEncryption=The PDF appears to have corrupted encryption data. This can happen when the PDF was created with incompatible encryption methods. Please try using the 'Repair PDF' feature first, or contact the document creator for a new copy.
error.fileProcessing=An error occurred while processing the file during {0} operation: {1}
error.pdfEncryption=Похоже, что в PDF-файле повреждены данные для шифрования. Это может произойти, если PDF-файл был создан с использованием несовместимых методов шифрования. Пожалуйста, сначала попробуйте воспользоваться функцией "Восстановить PDF" или обратитесь к создателю документа за новой копией.
error.fileProcessing=При обработке файла во время операции {0} произошла ошибка: {1}
# Generic error message templates
error.toolNotInstalled={0} is not installed
error.toolRequired={0} is required for {1}
error.conversionFailed={0} conversion failed
error.commandFailed={0} command failed
error.algorithmNotAvailable={0} algorithm not available
error.optionsNotSpecified={0} options are not specified
error.fileFormatRequired=File must be in {0} format
error.invalidFormat=Invalid {0} format: {1}
error.endpointDisabled=This endpoint has been disabled by the admin
error.urlNotReachable=URL is not reachable, please provide a valid URL
error.toolNotInstalled={0} не установлен
error.toolRequired={0} требуется для {1}
error.conversionFailed={0} не удалось выполнить преобразование
error.commandFailed={0} команда не выполнена
error.algorithmNotAvailable={0} алгоритм недоступен
error.optionsNotSpecified={0} параметры не указаны
error.fileFormatRequired=Файл должен быть в формате {0}
error.invalidFormat=Недопустимый формат {0}: {1}
error.endpointDisabled=Эта конечная точка была отключена администратором
error.urlNotReachable=URL-адрес недоступен, пожалуйста, укажите действительный URL-адрес
# DPI and image rendering messages - used by frontend for dynamic translation
# Backend sends: [TRANSLATE:messageKey:arg1,arg2] English message
# Frontend parses this and replaces with localized versions using these keys
error.dpiExceedsLimit=DPI value {0} exceeds maximum safe limit of {1}. High DPI values can cause memory issues and crashes. Please use a lower DPI value.
error.pageTooBigForDpi=PDF page {0} is too large to render at {1} DPI. Please try a lower DPI value (recommended: 150 or less).
error.pageTooBigExceedsArray=PDF page {0} is too large to render at {1} DPI. The resulting image would exceed Java's maximum array size. Please try a lower DPI value (recommended: 150 or less).
error.pageTooBigFor300Dpi=PDF page {0} is too large to render at 300 DPI. The resulting image would exceed Java's maximum array size. Please use a lower DPI value for PDF-to-image conversion.
error.dpiExceedsLimit=Значение DPI {0} превышает максимально допустимое значение {1}. Высокие значения DPI могут вызвать проблемы с памятью и сбои в работе. Пожалуйста, используйте меньшее значение DPI.
error.pageTooBigForDpi=Размер PDF-страницы {0} слишком велик для отображения с разрешением {1}. Пожалуйста, попробуйте использовать более низкое значение разрешения (рекомендуется 150 или меньше).
error.pageTooBigExceedsArray=Размер страницы PDF {0} слишком велик для отображения с разрешением {1} DPI. Результирующее изображение превысит максимальный размер массива, поддерживаемый Java. Пожалуйста, попробуйте использовать более низкое значение DPI (рекомендуется 150 или меньше).
error.pageTooBigFor300Dpi=Размер страницы PDF {0} слишком велик для отображения с разрешением 300 точек на дюйм. Результирующее изображение превысит максимальный размер массива, поддерживаемый Java. Пожалуйста, используйте меньшее значение разрешения для преобразования PDF в изображение.
# URL and website conversion messages
# System requirements messages
# Authentication and security messages
error.apiKeyInvalid=API key is not valid.
error.userNotFound=User not found.
error.passwordRequired=Password must not be null.
error.accountLocked=Your account has been locked due to too many failed login attempts.
error.invalidEmail=Invalid email addresses provided.
error.emailAttachmentRequired=An attachment is required to send the email.
error.signatureNotFound=Signature file not found.
error.apiKeyInvalid=Ключ API недействителен.
error.userNotFound=Пользователь не найден.
error.passwordRequired=Пароль не должен быть пустым.
error.accountLocked=Ваша учетная запись была заблокирована из-за слишком большого количества неудачных попыток входа в систему.
error.invalidEmail=Указанный адрес электронной почты неверный.
error.emailAttachmentRequired=Для отправки электронного письма требуется вложение.
error.signatureNotFound=Файл подписи не найден.
# File processing messages
error.fileNotFound=File not found with ID: {0}
error.fileNotFound=Файл с идентификатором: {0} не найден
# Database and configuration messages
error.noBackupScripts=No backup scripts were found.
error.unsupportedProvider={0} is not currently supported.
error.noBackupScripts=Сценарий резервного копирования не найден
error.unsupportedProvider={0} в настоящее время не поддерживается.
error.pathTraversalDetected=Path traversal detected for security reasons.
# Validation messages
error.invalidArgument=Invalid argument: {0}
error.argumentRequired={0} must not be null
error.operationFailed=Operation failed: {0}
error.angleNotMultipleOf90=Angle must be a multiple of 90
error.pdfBookmarksNotFound=No PDF bookmarks/outline found in document
error.fontLoadingFailed=Error processing font file
error.fontDirectoryReadFailed=Failed to read font directory
error.invalidArgument=Недопустимый аргумент: {0}
error.argumentRequired={0} не должно быть null
error.operationFailed=Операция завершилась неудачей: {0}
error.angleNotMultipleOf90=Угол наклона должен быть кратным 90
error.pdfBookmarksNotFound=В PDF-документе не найдены закладки/сноски
error.fontLoadingFailed=Ошибка при обработке файла шрифта
error.fontDirectoryReadFailed=Не удалось прочитать каталог шрифтов
delete=Удалить
username=Имя пользователя
password=Пароль
@ -260,7 +260,7 @@ disabledCurrentUserMessage=Текущий пользователь не може
downgradeCurrentUserLongMessage=Невозможно понизить роль текущего пользователя. Следовательно, текущий пользователь не будет отображаться.
userAlreadyExistsOAuthMessage=Пользователь уже существует как пользователь OAuth2.
userAlreadyExistsWebMessage=Пользователь уже существует как веб-пользователь.
invalidRoleMessage=Invalid role.
invalidRoleMessage=Недопустимая роль.
error=Ошибка
oops=Упс!
help=Помощь
@ -273,27 +273,27 @@ color=Цвет
sponsor=Спонсор
info=Информация
pro=Pro
proFeatures=Pro Features
proFeatures=Pro-функции
page=Страница
pages=Страницы
loading=Загрузка...
addToDoc=Добавить в документ
reset=Сбросить
apply=Применить
noFileSelected=No file selected. Please upload one.
view=View
cancel=Cancel
noFileSelected=Файл не выбран. Пожалуйста, загрузите его.
view=Смотреть
cancel=Закрыть
back.toSettings=Back to Settings
back.toHome=Back to Home
back.toAdmin=Back to Admin
back.toSettings=Вернуться к настройкам
back.toHome=Вернуться на главную
back.toAdmin=Вернуться в админку
legal.privacy=Политика конфиденциальности
legal.terms=Условия использования
legal.accessibility=Доступность
legal.cookie=Политика использования файлов cookie
legal.impressum=Выходные данные
legal.showCookieBanner=Cookie Preferences
legal.showCookieBanner=Настройки файлов cookie
###############
# Pipeline #
@ -327,7 +327,7 @@ enterpriseEdition.button=Перейти на Pro
enterpriseEdition.warning=Эта функция доступна только для пользователей Pro.
enterpriseEdition.yamlAdvert=Stirling PDF Pro поддерживает файлы конфигурации YAML и другие функции SSO.
enterpriseEdition.ssoAdvert=Ищете больше возможностей управления пользователями? Посмотрите Stirling PDF Pro
enterpriseEdition.proTeamFeatureDisabled=Team management features require a Pro licence or higher
enterpriseEdition.proTeamFeatureDisabled=Для функций управления группой требуется лицензия Pro или выше
#################
@ -408,8 +408,8 @@ account.property=Свойство
account.webBrowserSettings=Настройки веб-браузера
account.syncToBrowser=Синхронизировать Аккаунт -> Браузер
account.syncToAccount=Синхронизировать Аккаунт <- Браузер
account.adminTitle=Administrator Tools
account.adminNotif=You have admin privileges. Access system settings and user management.
account.adminTitle=Инструменты администратора
account.adminNotif=У вас есть права администратора. Вам доступны системные настройки и управление пользователями.
adminUserSettings.title=Настройки управления пользователями
@ -441,18 +441,18 @@ adminUserSettings.totalUsers=Всего пользователей:
adminUserSettings.lastRequest=Последний запрос
adminUserSettings.usage=View Usage
adminUserSettings.teams=View/Edit Teams
adminUserSettings.team=Team
adminUserSettings.manageTeams=Manage Teams
adminUserSettings.createTeam=Create Team
adminUserSettings.viewTeam=View Team
adminUserSettings.deleteTeam=Delete Team
adminUserSettings.teamName=Team Name
adminUserSettings.teamExists=Team already exists
adminUserSettings.teamCreated=Team created successfully
adminUserSettings.teamChanged=User's team was updated
adminUserSettings.teamHidden=Hidden
adminUserSettings.totalMembers=Total Members
adminUserSettings.confirmDeleteTeam=Are you sure you want to delete this team?
adminUserSettings.team=Группа
adminUserSettings.manageTeams=Управление группами
adminUserSettings.createTeam=Создать группу
adminUserSettings.viewTeam=Смотреть группу
adminUserSettings.deleteTeam=Удалить группу
adminUserSettings.teamName=Имя группы
adminUserSettings.teamExists=Группа уже существует
adminUserSettings.teamCreated=Группа успешно создана
adminUserSettings.teamChanged=Группа пользователей была обновлена
adminUserSettings.teamHidden=Скрытая
adminUserSettings.totalMembers=Общее количество участников
adminUserSettings.confirmDeleteTeam=Вы уверены, что хотите удалить эту группу?
teamCreated=Team created successfully
teamExists=A team with that name already exists
@ -538,18 +538,18 @@ home.desc=Ваше локальное решение для всех потре
home.searchBar=Поиск функций...
home.viewPdf.title=View/Edit PDF
home.viewPdf.title=Смотреть/Редактировать PDF
home.viewPdf.desc=Просмотр, аннотирование, добавление текста или изображений
viewPdf.tags=просмотр,чтение,аннотации,текст,изображение
home.setFavorites=Set Favourites
home.hideFavorites=Hide Favourites
home.showFavorites=Show Favourites
home.legacyHomepage=Old homepage
home.newHomePage=Try our new homepage!
home.alphabetical=Alphabetical
home.globalPopularity=Global Popularity
home.sortBy=Sort by:
home.setFavorites=Добавить в избранное
home.hideFavorites=Скрыть из избранного
home.showFavorites=Показать избранное
home.legacyHomepage=Старый вид главной страницы
home.newHomePage=Попробуйте нашу новую главную страницу!
home.alphabetical=Алфавиту
home.globalPopularity=Популярности
home.sortBy=Сортировать по:
home.multiTool.title=Мультиинструмент PDF
home.multiTool.desc=Объединение, поворот, переупорядочивание и удаление страниц
@ -585,8 +585,8 @@ home.addImage.title=Добавить изображение
home.addImage.desc=Добавляет изображение в указанное место PDF
addImage.tags=изображение,jpg,картинка,фото
home.attachments.title=Add Attachments
home.attachments.desc=Add or remove embedded files (attachments) to/from a PDF
home.attachments.title=Добавлять вложения
home.attachments.desc=Добавление или удаление встроенных файлов (вложений) в PDF-файл или из него
attachments.tags=embed,attach,file,attachment,attachments
home.watermark.title=Добавить водяной знак
@ -614,8 +614,8 @@ home.compressPdfs.title=Сжать
home.compressPdfs.desc=Сжимайте PDF-файлы для уменьшения их размера.
compressPdfs.tags=сжатие,маленький,крошечный
home.unlockPDFForms.title=Unlock PDF Forms
home.unlockPDFForms.desc=Remove read-only property of form fields in a PDF document.
home.unlockPDFForms.title=Разблокировать PDF-формы
home.unlockPDFForms.desc=Удалите свойство "только для чтения" для полей формы в PDF-документа.
unlockPDFForms.tags=remove,delete,form,field,readonly
home.changeMetadata.title=Изменить метаданные
@ -740,20 +740,20 @@ home.HTMLToPDF.desc=Преобразует любой HTML-файл или zip
HTMLToPDF.tags=разметка,веб-контент,преобразование,конвертация
#eml-to-pdf
home.EMLToPDF.title=Email to PDF
home.EMLToPDF.desc=Converts email (EML) files to PDF format including headers, body, and inline images
home.EMLToPDF.title=Email в PDF
home.EMLToPDF.desc=Преобразует файлы электронной почты (EML) в формат PDF, включая заголовки, основную часть и встроенные изображения
EMLToPDF.tags=email,conversion,eml,message,transformation,convert,mail
EMLToPDF.title=Email To PDF
EMLToPDF.header=Email To PDF
EMLToPDF.submit=Convert
EMLToPDF.downloadHtml=Download HTML intermediate file instead of PDF
EMLToPDF.downloadHtmlHelp=This allows you to see the HTML version before PDF conversion and can help debug formatting issues
EMLToPDF.includeAttachments=Include attachments in PDF
EMLToPDF.maxAttachmentSize=Maximum attachment size (MB)
EMLToPDF.help=Converts email (EML) files to PDF format including headers, body, and inline images
EMLToPDF.troubleshootingTip1=Email to HTML is a more reliable process, so with batch-processing it is recommended to save both
EMLToPDF.troubleshootingTip2=With a small number of Emails, if the PDF is malformed, you can download HTML and override some of the problematic HTML/CSS code.
EMLToPDF.title=Email в PDF
EMLToPDF.header=Email в PDF
EMLToPDF.submit=Преобразовать
EMLToPDF.downloadHtml=Загрузить промежуточный файл HTML вместо PDF
EMLToPDF.downloadHtmlHelp=Это позволит вам просмотреть HTML-версию перед преобразованием в PDF и поможет устранить проблемы с форматированием
EMLToPDF.includeAttachments=Включать вложения в формате PDF
EMLToPDF.maxAttachmentSize=Максимальный размер вложения (MB)
EMLToPDF.help=Преобразует файлы электронной почты (EML) в формат PDF, включая заголовки, основную часть и встроенные изображения
EMLToPDF.troubleshootingTip1=Электронная почта в формате HTML является более надежным процессом, поэтому при пакетной обработке рекомендуется сохранять оба
EMLToPDF.troubleshootingTip2=При небольшом количестве электронных писем, если формат PDF искажен, вы можете загрузить HTML и переопределить часть проблемного HTML/CSS-кода.
EMLToPDF.troubleshootingTip3=Embeddings, however, do not work with HTMLs
home.MarkdownToPDF.title=Markdown в PDF
@ -761,7 +761,7 @@ home.MarkdownToPDF.desc=Преобразует любой файл Markdown в P
MarkdownToPDF.tags=разметка,веб-контент,преобразование,конвертация
home.PDFToMarkdown.title=PDF to Markdown
home.PDFToMarkdown.desc=Converts any PDF to Markdown
home.PDFToMarkdown.desc=Преобразует любой PDF-файл в формат Markdown
PDFToMarkdown.tags=markup,web-content,transformation,convert,md
home.getPdfInfo.title=Получить ВСЮ информацию о PDF
@ -875,7 +875,7 @@ login.userIsDisabled=Пользователь деактивирован, вхо
login.alreadyLoggedIn=Вы уже вошли в
login.alreadyLoggedIn2=устройств(а). Пожалуйста, выйдите из этих устройств и попробуйте снова.
login.toManySessions=У вас слишком много активных сессий
login.logoutMessage=You have been logged out.
login.logoutMessage=Вы вышли из системы.
#auto-redact
autoRedact.title=Автоматическое редактирование
@ -914,7 +914,7 @@ redact.showAttatchments=Показать вложения
redact.showLayers=Показать слои (двойной щелчок для сброса всех слоев к состоянию по умолчанию)
redact.colourPicker=Выбор цвета
redact.findCurrentOutlineItem=Найти текущий элемент структуры
redact.applyChanges=Apply Changes
redact.applyChanges=Применить изменения
#showJS
showJS.title=Показать Javascript
@ -942,15 +942,15 @@ getPdfInfo.header=Получить информацию о PDF
getPdfInfo.submit=Получить информацию
getPdfInfo.downloadJson=Скачать JSON
getPdfInfo.summary=PDF Summary
getPdfInfo.summary.encrypted=This PDF is encrypted so may face issues with some applications
getPdfInfo.summary.permissions=This PDF has {0} restricted permissions which may limit what you can do with it
getPdfInfo.summary.compliance=This PDF complies with the {0} standard
getPdfInfo.summary.basicInfo=Basic Information
getPdfInfo.summary.docInfo=Document Information
getPdfInfo.summary.encrypted.alert=Encrypted PDF - This document is password protected
getPdfInfo.summary.not.encrypted.alert=Unencrypted PDF - No password protection
getPdfInfo.summary.permissions.alert=Restricted Permissions - {0} actions are not allowed
getPdfInfo.summary.all.permissions.alert=All Permissions Allowed
getPdfInfo.summary.encrypted=Этот PDF-файл зашифрован, поэтому с некоторыми приложениями могут возникнуть проблемы
getPdfInfo.summary.permissions=Этот PDF-файл имеет {0} ограниченные права доступа, которые могут ограничить то, что вы можете с ним делать
getPdfInfo.summary.compliance=Этот PDF-файл соответствует стандарту {0}
getPdfInfo.summary.basicInfo=Основная информация
getPdfInfo.summary.docInfo=Информация о документе
getPdfInfo.summary.encrypted.alert=Зашифрованный PDF-файл - этот документ защищен паролем
getPdfInfo.summary.not.encrypted.alert=Незашифрованный PDF-файл - без защиты паролем
getPdfInfo.summary.permissions.alert=Права доступа ограничены - {0} действия запрещены
getPdfInfo.summary.all.permissions.alert=Полные права доступа
getPdfInfo.summary.compliance.alert={0} Compliant
getPdfInfo.summary.no.compliance.alert=No Compliance Standards
getPdfInfo.summary.security.section=Security Status
@ -974,9 +974,9 @@ MarkdownToPDF.credit=Использует WeasyPrint
#pdf-to-markdown
PDFToMarkdown.title=PDF To Markdown
PDFToMarkdown.header=PDF To Markdown
PDFToMarkdown.submit=Convert
PDFToMarkdown.title=PDF в Markdown
PDFToMarkdown.header=PDF в Markdown
PDFToMarkdown.submit=Преобразовать
#url-to-pdf
@ -1030,10 +1030,10 @@ sanitizePDF.title=Очистить PDF
sanitizePDF.header=Очистить PDF-файл
sanitizePDF.selectText.1=Удалить JavaScript-действия
sanitizePDF.selectText.2=Удалить встроенные файлы
sanitizePDF.selectText.3=Remove XMP metadata
sanitizePDF.selectText.3=Удалить XMP метаданные
sanitizePDF.selectText.4=Удалить ссылки
sanitizePDF.selectText.5=Удалить шрифты
sanitizePDF.selectText.6=Remove Document Info Metadata
sanitizePDF.selectText.6=Удалить метаданные с информацией о документе
sanitizePDF.submit=Очистить PDF
@ -1182,8 +1182,8 @@ sign.last=Последняя страница
sign.next=Следующая страница
sign.previous=Предыдущая страница
sign.maintainRatio=Переключить сохранение пропорций
sign.undo=Undo
sign.redo=Redo
sign.undo=Отменить
sign.redo=Повторить
#repair
repair.title=Восстановление
@ -1254,8 +1254,8 @@ compress.title=Сжать
compress.header=Сжать PDF
compress.credit=Этот сервис использует qpdf для сжатия/оптимизации PDF.
compress.grayscale.label=Применить шкалу серого для сжатия
compress.selectText.1=Compression Settings
compress.selectText.1.1=1-3 PDF compression,</br> 4-6 lite image compression,</br> 7-9 intense image compression Will dramatically reduce image quality
compress.selectText.1=Параметры сжатия
compress.selectText.1.1=1-3 сжатие PDF,</br> 4-6 лёгкое сжатие изображений,</br> 7-9 интенсивное сжатие изображений (значительно снижает качество изображений)
compress.selectText.2=Уровень оптимизации:
compress.selectText.4=Автоматический режим - автоматически настраивает качество для получения точного размера PDF
compress.selectText.5=Ожидаемый размер PDF (например, 25MB, 10.8MB, 25KB)
@ -1270,11 +1270,11 @@ addImage.upload=Добавить изображение
addImage.submit=Добавить изображение
#attachments
attachments.title=Add Attachments
attachments.header=Add attachments
attachments.description=Allows you to add attachments to the PDF
attachments.descriptionPlaceholder=Enter a description for the attachments...
attachments.addButton=Add Attachments
attachments.title=Добавлять вложения
attachments.header=Добавлять вложения
attachments.description=Позволяет добавлять вложения в PDF-файл
attachments.descriptionPlaceholder=Введите описание для вложений...
attachments.addButton=Добавлять вложения
#merge
merge.title=Объединить
@ -1282,7 +1282,7 @@ merge.header=Объединение нескольких PDF (2+)
merge.sortByName=Сортировать по имени
merge.sortByDate=Сортировать по дате
merge.removeCertSign=Удалить цифровую подпись в объединенном файле?
merge.generateToc=Generate table of contents in the merged file?
merge.generateToc=Сгенерировать оглавление в объединенном файле?
merge.submit=Объединить
@ -1301,7 +1301,7 @@ pdfOrganiser.mode.7=Удалить первую
pdfOrganiser.mode.8=Удалить последнюю
pdfOrganiser.mode.9=Удалить первую и последнюю
pdfOrganiser.mode.10=Объединение четных-нечетных
pdfOrganiser.mode.11=Duplicate all pages
pdfOrganiser.mode.11=Дублировать все страницы
pdfOrganiser.placeholder=(например, 1,3,2 или 4-8,2,10-12 или 2n-1)
@ -1344,7 +1344,7 @@ decrypt.success=Файл успешно расшифрован.
multiTool-advert.message=Эта функция также доступна на нашей <a href="{0}">странице мультиинструмента</a>. Попробуйте её для улучшенного постраничного интерфейса и дополнительных возможностей!
#view pdf
viewPdf.title=View/Edit PDF
viewPdf.title=Смотреть/Редактировать PDF
viewPdf.header=Просмотр PDF
#pageRemover
@ -1492,9 +1492,9 @@ changeMetadata.selectText.5=Добавить пользовательскую з
changeMetadata.submit=Изменить
#unlockPDFForms
unlockPDFForms.title=Remove Read-Only from Form Fields
unlockPDFForms.header=Unlock PDF Forms
unlockPDFForms.submit=Remove
unlockPDFForms.title=Удалить поля формы, доступные только для чтения
unlockPDFForms.header=Разблокировать PDF-формы
unlockPDFForms.submit=Удалить
#pdfToPDFA
pdfToPDFA.title=PDF в PDF/A
@ -1724,12 +1724,12 @@ audit.dashboard.tab.dashboard=Dashboard
audit.dashboard.tab.events=Audit Events
audit.dashboard.tab.export=Export
# Dashboard Charts
audit.dashboard.eventsByType=Events by Type
audit.dashboard.eventsByUser=Events by User
audit.dashboard.eventsOverTime=Events Over Time
audit.dashboard.period.7days=7 Days
audit.dashboard.period.30days=30 Days
audit.dashboard.period.90days=90 Days
audit.dashboard.eventsByType=События по типу
audit.dashboard.eventsByUser=События по пользователю
audit.dashboard.eventsOverTime=События за всё время
audit.dashboard.period.7days=7 дней
audit.dashboard.period.30days=30 дней
audit.dashboard.period.90days=90 дней
# Events Tab
audit.dashboard.auditEvents=Audit Events
@ -1812,49 +1812,48 @@ cookieBanner.preferencesModal.analytics.title=Analytics
cookieBanner.preferencesModal.analytics.description=These cookies help us understand how our tools are being used, so we can focus on building the features our community values most. Rest assured—Stirling PDF cannot and will never track the content of the documents you work with.
#scannerEffect
scannerEffect.title=Scanner Effect
scannerEffect.header=Scanner Effect
scannerEffect.description=Create a PDF that looks like it was scanned
scannerEffect.selectPDF=Select PDF:
scannerEffect.quality=Scan Quality
scannerEffect.quality.low=Low
scannerEffect.quality.medium=Medium
scannerEffect.quality.high=High
scannerEffect.rotation=Rotation Angle
scannerEffect.rotation.none=None
scannerEffect.rotation.slight=Slight
scannerEffect.rotation.moderate=Moderate
scannerEffect.rotation.severe=Severe
scannerEffect.submit=Create Scanner Effect
scannerEffect.title=Поддельное сканирование
scannerEffect.header=Поддельное сканирование
scannerEffect.description=Создайте PDF-файл, который выглядит так, как будто он был отсканирован
scannerEffect.selectPDF=Выбрать PDF:
scannerEffect.quality=Качество сканирования
scannerEffect.quality.low=Низкое
scannerEffect.quality.medium=Среднее
scannerEffect.quality.high=Хорошее
scannerEffect.rotation=Угол поворота
scannerEffect.rotation.none=Нет
scannerEffect.rotation.slight=Незначительный
scannerEffect.rotation.moderate=Умеренный
scannerEffect.rotation.severe=Сильный
scannerEffect.submit=Создать поддельное сканирование
#home.scannerEffect
home.scannerEffect.title=Scanner Effect
home.scannerEffect.desc=Create a PDF that looks like it was scanned
home.scannerEffect.title=Поддельное сканирование
home.scannerEffect.desc=Создайте PDF-файл, который выглядит так, как будто он был отсканирован
scannerEffect.tags=scan,simulate,realistic,convert
# ScannerEffect advanced settings (frontend)
scannerEffect.advancedSettings=Enable Advanced Scan Settings
scannerEffect.colorspace=Colorspace
scannerEffect.colorspace.grayscale=Grayscale
scannerEffect.colorspace.color=Color
scannerEffect.border=Border (px)
scannerEffect.rotate=Base Rotation (degrees)
scannerEffect.rotateVariance=Rotation Variance (degrees)
scannerEffect.brightness=Brightness
scannerEffect.contrast=Contrast
scannerEffect.blur=Blur
scannerEffect.noise=Noise
scannerEffect.yellowish=Yellowish (simulate old paper)
scannerEffect.resolution=Resolution (DPI)
# scannerEffect advanced settings (frontend)
scannerEffect.advancedSettings=Включите расширенные параметры сканирования
scannerEffect.colorspace=Цветовое пространство
scannerEffect.colorspace.grayscale=Оттенки серого
scannerEffect.colorspace.color=Цветное
scannerEffect.border=Рамка (px)
scannerEffect.rotate=Базовый наклон (degrees)
scannerEffect.rotateVariance=Скорость вращения (degrees)
scannerEffect.brightness=Яркость
scannerEffect.contrast=Контраст
scannerEffect.blur=Размытие
scannerEffect.noise=Шум
scannerEffect.yellowish=Желтоватый оттенок (имитация старой бумаги)
scannerEffect.resolution=Разрешение (DPI)
# Table of Contents Feature
home.editTableOfContents.title=Edit Table of Contents
home.editTableOfContents.desc=Add or edit bookmarks and table of contents in PDF documents
home.editTableOfContents.title=Редактировать оглавление
home.editTableOfContents.desc=Добавление или редактирование закладок и оглавления в PDF-документах
editTableOfContents.tags=bookmarks,toc,navigation,index,table of contents,chapters,sections,outline
editTableOfContents.title=Edit Table of Contents
editTableOfContents.header=Add or Edit PDF Table of Contents
editTableOfContents.title=Редактировать оглавление
editTableOfContents.header=Добавление или редактирование закладок и оглавления в PDF-документах
editTableOfContents.replaceExisting=Replace existing bookmarks (uncheck to append to existing)
editTableOfContents.editorTitle=Bookmark Editor
editTableOfContents.editorDesc=Add and arrange bookmarks below. Click + to add child bookmarks.

View File

@ -159,41 +159,36 @@
.scalable-languages-container {
display: grid;
/* Auto-fill columns, with a minimum width of 180px */
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
grid-template-columns: repeat(1, 1fr);
}
@media (min-width: 400px) {
#languageSelection.scalable-languages-container {
grid-template-columns: repeat(2, 1fr);
}
}
@media (min-width: 600px) {
#languageSelection.scalable-languages-container {
grid-template-columns: repeat(3, 1fr);
}
}
@media (min-width: 900px) {
#languageSelection.scalable-languages-container {
grid-template-columns: repeat(4, 1fr) !important;
}
}
.scalable-languages-container:not(:has(> :nth-child(4))) .lang-dropdown-item-wrapper:last-child {
border: 0px !important
}
.scalable-languages-container:has(> *:nth-child(1)) {
--count: 1;
html[dir="ltr"] #languageSelection .lang-dropdown-item-wrapper:last-child {
border-right: none !important;
}
.scalable-languages-container:has(> *:nth-child(2)) {
--count: 2;
#languageSelection .lang-dropdown-item-wrapper:last-child {
border: 0px !important;
}
.scalable-languages-container:has(> *:nth-child(3)) {
--count: 3;
}
html[dir="ltr"] .lang-dropdown-item-wrapper {
border-right: 2px solid var(--md-nav-color-on-separator);
}
html[dir="rtl"] .lang-dropdown-item-wrapper {
border-left: 2px solid var(--md-nav-color-on-separator);
}
/* Responsive adjustments */
@media (min-width: 1200px) {
.lang-dropdown-item-wrapper .dropdown-item {
min-width: 200px
}
}
@media (max-width: 600px) {
.scalable-languages-container {
grid-template-columns: repeat(2, 1fr);
@ -236,6 +231,41 @@ html[dir="rtl"] .lang-dropdown-item-wrapper {
}
}
.scalable-languages-container:has(> *:nth-child(1)) {
--count: 1;
}
.scalable-languages-container:has(> *:nth-child(2)) {
--count: 2;
}
.scalable-languages-container:has(> *:nth-child(3)) {
--count: 3;
}
html[dir="ltr"] .lang-dropdown-item-wrapper {
border-right: 2px solid var(--md-nav-color-on-separator);
}
html[dir="rtl"] .lang-dropdown-item-wrapper {
border-left: 2px solid var(--md-nav-color-on-separator);
}
/* Responsive adjustments */
@media (min-width: 1200px) {
.lang-dropdown-item-wrapper .dropdown-item {
min-width: 200px
}
.scroll-lock-y {
overflow-y: auto;
max-height: 80vh;
overscroll-behavior-y: contain;
-webkit-overflow-scrolling: touch;
}
}
.dropdown-item .icon-text {
text-wrap: wrap;
word-break: break-word;
@ -290,6 +320,21 @@ span.icon-text::after {
color: var(--md-sys-color-on-surface-variant);
}
.nav-link {
display: flex;
align-items: center;
max-width: 98vw;
}
.chevron-icon {
margin-left: auto;
transition: transform 0.3s ease;
}
[aria-expanded="true"] > .chevron-icon {
transform: rotate(180deg);
}
.nav-item {
position: relative;
}
@ -473,6 +518,7 @@ html[dir="rtl"] .dropdown-menu {
box-shadow: var(--md-sys-elevation-2);
}
.dropdown-menu-tp {
color: transparent;
background-color: transparent;
@ -484,27 +530,119 @@ html[dir="rtl"] .dropdown-menu {
display: inline-flex;
}
@media (min-width:992px) {
@media (max-width:1199.98px) {
.navbar-collapse .dropdown-menu {
width: 100%;
}
.navbar-collapse .dropdown-menu-wrapper {
width: 100%;
box-sizing: border-box;
}
.navbar-collapse .dropdown-mw-28 {
min-width: 0;
}
}
@media (min-width:1200px) {
/* This CSS-based hover is disabled because it conflicts with Bootstrap's JavaScript.
Hover functionality is now handled in navbar.js and search.js */
/*
.dropdown:hover .dropdown-menu {
display: block;
margin-top: 0;
}
*/
/* .icon-hide {
display: none;
} */
}
@media (max-width:1199px) {
.icon-hide {
display: inline-flex;
}
}
@media (min-width:1200px) {
.icon-hide {
display: none;
}
.chevron-icon {
display: none !important;
}
}
@media (max-width: 1199.98px) {
.navbar-collapse .dropdown-menu {
width: 100vw !important;
max-width: 100vw !important;
left: 0 !important;
right: 0 !important;
transform: none !important;
transform-origin: none !important;
}
.navbar .navbar-expand-xl .dropdown-menu,
.navbar-expand .dropdown-menu {
transform: none !important;
transform-origin: none !important;
left: 0 !important;
right: 0 !important;
}
.navbar-collapse .dropdown-mega .dropdown-menu {
max-height: 60vh;
overflow-y: auto;
}
.navbar-collapse .dropdown-mega .dropdown-menu-wrapper {
border-radius: 0;
}
#favoritesDropdown,
#languageDropdown + .dropdown-menu .dropdown-menu-wrapper,
#searchDropdown + .dropdown-menu .dropdown-menu-wrapper {
padding: 1.5rem 1rem;
}
#favoritesDropdown, #languageSelection, #searchResults {
width: 100%;
box-sizing: border-box;
}
.navbar-collapse .dropdown-menu-wrapper {
width: 95vw;
box-sizing: border-box;
margin-left: 0 !important;
padding: 0 !important;
}
.navbar-collapse .dropdown-mw-28 {
min-width: 0;
}
.icon-hide {
display: inline-flex;
}
.navbar-collapse .dropdown-item {
margin-left: 0 !important;
}
.container {
margin-left: auto !important;
margin-right: auto !important;
padding-left: 4px !important;
}
#mainNavbarDropdownMenu {
transform: none !important;
transform-origin: none !important;
left: 0 !important;
right: 0 !important;
width: 100vw !important;
margin-bottom: 0 !important;
}
.dropdown-menu,
.dropdown-menu-tp,
.dropdown-menu-tp.show {
transform: none !important;
transform-origin: none !important;
max-width: 95vw !important;
width: 100vw !important;
left: 0 !important;
right: 0 !important;
margin-bottom: 0 !important;
}
}
.go-pro-link {
@ -558,6 +696,12 @@ html[dir="rtl"] .dropdown-menu {
box-sizing: border-box;
}
@media (max-width: 768px) {
.feature-group {
min-width: 10rem;
}
}
.feature-rows {
display: flex;
flex-wrap: wrap;

View File

@ -40,7 +40,7 @@ textarea {
}
*::-webkit-scrollbar-corner {
background-color: var(--md-sys-color-surface);
background-color: var(--md-sys-color-surface);
}
/* Alerts */
@ -66,6 +66,9 @@ td {
background-color: var(--md-sys-color-surface-5);
border-radius: 3rem;
padding: 2.5rem;
max-width: 95vw;
margin-left: 2vw;
}
.card {

View File

@ -44,8 +44,16 @@ function updateFavoritesDropdown() {
contentWrapper.style.color = 'inherit';
// Clone the original content
var originalContent = navbarEntry.querySelector('div').cloneNode(true);
contentWrapper.appendChild(originalContent);
var divElement = navbarEntry.querySelector('div');
if (divElement) {
var originalContent = divElement.cloneNode(true);
contentWrapper.appendChild(originalContent);
} else {
// Fallback: create content manually if div is not found
var fallbackContent = document.createElement('div');
fallbackContent.innerHTML = navbarEntry.innerHTML;
contentWrapper.appendChild(fallbackContent);
}
// Create the remove button
var removeButton = document.createElement('button');

View File

@ -42,6 +42,39 @@ function toolsManager() {
});
}
function setupDropdowns() {
const dropdowns = document.querySelectorAll('.navbar-nav > .nav-item.dropdown');
dropdowns.forEach((dropdown) => {
const toggle = dropdown.querySelector('[data-bs-toggle="dropdown"]');
if (!toggle) return;
// Skip search dropdown, it has its own logic
if (toggle.id === 'searchDropdown') {
return;
}
dropdown.addEventListener('show.bs.dropdown', () => {
// Find all other open dropdowns and hide them
const openDropdowns = document.querySelectorAll('.navbar-nav .dropdown-menu.show');
openDropdowns.forEach((menu) => {
const parentDropdown = menu.closest('.dropdown');
if (parentDropdown && parentDropdown !== dropdown) {
const parentToggle = parentDropdown.querySelector('[data-bs-toggle="dropdown"]');
if (parentToggle) {
// Get or create Bootstrap dropdown instance
let instance = bootstrap.Dropdown.getInstance(parentToggle);
if (!instance) {
instance = new bootstrap.Dropdown(parentToggle);
}
instance.hide();
}
}
});
});
});
}
window.tooltipSetup = () => {
const tooltipElements = document.querySelectorAll('[title]');
@ -56,23 +89,54 @@ window.tooltipSetup = () => {
document.body.appendChild(customTooltip);
element.addEventListener('mouseenter', (event) => {
customTooltip.style.display = 'block';
customTooltip.style.left = `${event.pageX + 10}px`; // Position tooltip slightly away from the cursor
customTooltip.style.top = `${event.pageY + 10}px`;
if (window.innerWidth >= 1200) {
customTooltip.style.display = 'block';
customTooltip.style.left = `${event.pageX + 10}px`;
customTooltip.style.top = `${event.pageY + 10}px`;
}
});
// Update the position of the tooltip as the user moves the mouse
element.addEventListener('mousemove', (event) => {
customTooltip.style.left = `${event.pageX + 10}px`;
customTooltip.style.top = `${event.pageY + 10}px`;
if (window.innerWidth >= 1200) {
customTooltip.style.left = `${event.pageX + 10}px`;
customTooltip.style.top = `${event.pageY + 10}px`;
}
});
// Hide the tooltip when the mouse leaves
element.addEventListener('mouseleave', () => {
customTooltip.style.display = 'none';
});
});
};
// Override the bootstrap dropdown styles for mobile
function fixNavbarDropdownStyles() {
if (window.innerWidth < 1200) {
document.querySelectorAll('.navbar .dropdown-menu').forEach(function(menu) {
menu.style.transform = 'none';
menu.style.transformOrigin = 'none';
menu.style.left = '0';
menu.style.right = '0';
menu.style.maxWidth = '95vw';
menu.style.width = '100vw';
menu.style.marginBottom = '0';
});
} else {
document.querySelectorAll('.navbar .dropdown-menu').forEach(function(menu) {
menu.style.transform = '';
menu.style.transformOrigin = '';
menu.style.left = '';
menu.style.right = '';
menu.style.maxWidth = '';
menu.style.width = '';
menu.style.marginBottom = '';
});
}
}
document.addEventListener('DOMContentLoaded', () => {
tooltipSetup();
setupDropdowns();
fixNavbarDropdownStyles();
});
window.addEventListener('resize', fixNavbarDropdownStyles);

View File

@ -56,8 +56,16 @@ document.querySelector("#navbarSearchInput").addEventListener("input", function
contentWrapper.style.textDecoration = "none";
contentWrapper.style.color = "inherit";
var originalContent = item.querySelector("div").cloneNode(true);
contentWrapper.appendChild(originalContent);
var divElement = item.querySelector("div");
if (divElement) {
var originalContent = divElement.cloneNode(true);
contentWrapper.appendChild(originalContent);
} else {
// Fallback: create content manually if div is not found
var fallbackContent = document.createElement("div");
fallbackContent.innerHTML = item.innerHTML;
contentWrapper.appendChild(fallbackContent);
}
contentWrapper.onclick = function () {
window.location.href = itemHref;
@ -77,35 +85,52 @@ document.querySelector("#navbarSearchInput").addEventListener("input", function
const searchDropdown = document.getElementById('searchDropdown');
const searchInput = document.getElementById('navbarSearchInput');
const dropdownMenu = searchDropdown.querySelector('.dropdown-menu');
// Handle dropdown shown event
searchDropdown.addEventListener('shown.bs.dropdown', function () {
searchInput.focus();
});
// Check if elements exist before proceeding
if (searchDropdown && searchInput) {
const dropdownMenu = searchDropdown.querySelector('.dropdown-menu');
// Handle hover opening
searchDropdown.addEventListener('mouseenter', function () {
// Create a single dropdown instance
const dropdownInstance = new bootstrap.Dropdown(searchDropdown);
dropdownInstance.show();
setTimeout(() => {
searchInput.focus();
}, 100);
});
// Handle click for mobile
searchDropdown.addEventListener('click', function (e) {
e.preventDefault();
const isOpen = dropdownMenu.classList.contains('show');
// Close all other open dropdowns
document.querySelectorAll('.navbar-nav .dropdown-menu.show').forEach((menu) => {
if (menu !== dropdownMenu) {
const parentDropdown = menu.closest('.dropdown');
if (parentDropdown) {
const parentToggle = parentDropdown.querySelector('[data-bs-toggle="dropdown"]');
if (parentToggle) {
let instance = bootstrap.Dropdown.getInstance(parentToggle);
if (!instance) {
instance = new bootstrap.Dropdown(parentToggle);
}
instance.hide();
}
}
}
});
if (!isOpen) {
dropdownInstance.show();
setTimeout(() => searchInput.focus(), 150);
} else {
dropdownInstance.hide();
}
});
// Handle mouse leave
searchDropdown.addEventListener('mouseleave', function () {
// Check if current value is empty (including if user typed and then deleted)
if (searchInput.value.trim().length === 0) {
searchInput.blur();
const dropdownInstance = new bootstrap.Dropdown(searchDropdown);
dropdownInstance.hide();
}
});
// Hide dropdown if it's open and user clicks outside
document.addEventListener('click', function(e) {
if (!searchDropdown.contains(e.target) && dropdownMenu.classList.contains('show')) {
dropdownInstance.hide();
}
});
searchDropdown.addEventListener('hidden.bs.dropdown', function () {
if (searchInput.value.trim().length === 0) {
searchInput.blur();
}
});
// Keep dropdown open if search input is clicked
searchInput.addEventListener('click', function (e) {
e.stopPropagation();
});
}

View File

@ -29,8 +29,40 @@
// Determine if this is actually a high DPI screen at page load
const isHighDPI = systemDPR > 1.4;
// Reset all navbar and dropdown scaling styles
function resetNavScaling() {
const navbarElement = document.querySelector('.navbar');
if (navbarElement) {
navbarElement.style.transform = '';
navbarElement.style.transformOrigin = '';
navbarElement.style.width = '';
navbarElement.style.left = '';
navbarElement.style.right = '';
navbarElement.style.marginBottom = '';
navbarElement.classList.remove('navbar-expand-lg');
navbarElement.classList.remove('navbar-expand-xl');
}
// Reset dropdown scaling
const dropdowns = document.querySelectorAll('.dropdown-menu');
dropdowns.forEach(dropdown => {
dropdown.style.transform = '';
dropdown.style.transformOrigin = '';
});
// Reset CSS custom property
document.documentElement.style.setProperty('--navbar-height', '');
}
function scaleNav() {
resetNavScaling();
if (window.innerWidth < 1200) {
const navbarElement = document.querySelector('.navbar');
if (navbarElement) {
navbarElement.classList.remove('navbar-expand-lg');
navbarElement.classList.add('navbar-expand-xl');
}
return;
}
const currentDPR = window.devicePixelRatio || 1;
const browserZoom = currentDPR / systemDPR;

View File

@ -45,9 +45,10 @@
apps
</span>
<span class="icon-text" th:data-text="#{navbar.allTools}" th:text="#{navbar.allTools}"></span>
<span class="material-symbols-rounded chevron-icon">expand_more</span>
</a>
<div class="dropdown-menu dropdown-menu-tp" aria-labelledby="navbarDropdown-1">
<div class="dropdown-menu-wrapper" style="justify-content: center; display:flex">
<div class="dropdown-menu-wrapper scroll-lock-y" style="max-width: 95vw !important;">
<div class="feature-rows">
<th:block th:insert="~{fragments/navElements.html :: navElements}"></th:block>
@ -117,9 +118,10 @@
star
</span>
<span class="icon-text icon-hide" th:data-text="#{navbar.favorite}" th:text="#{navbar.favorite}"></span>
<span class="material-symbols-rounded chevron-icon">expand_more</span>
</a>
<div class="dropdown-menu dropdown-menu-tp dropdown-mw-28" aria-labelledby="navbarDropdown-5">
<div class="dropdown-menu-wrapper px-xl-2 px-2" id="favoritesDropdown">
<div class="dropdown-menu dropdown-menu-tp dropdown-mw-28" role="menu" aria-labelledby="navbarDropdown-5">
<div class="dropdown-menu-wrapper px-xl-2 px-2 scroll-lock-y" id="favoritesDropdown" style="max-width: 95vw !important; ">
<!-- Dropdown items will be added here by JavaScript -->
</div>
</div>
@ -140,9 +142,10 @@
language
</span>
<span class="icon-text icon-hide" th:data-text="#{navbar.language}" th:text="#{navbar.language}"></span>
<span class="material-symbols-rounded chevron-icon">expand_more</span>
</a>
<div class="dropdown-menu dropdown-menu-tp" aria-labelledby="languageDropdown">
<div class="dropdown-menu-wrapper px-xl-2 px-2">
<div class="dropdown-menu-wrapper px-xl-2 px-2 scroll-lock-y" style="max-width: 95vw !important;">
<div id="languageSelection" class="scrollable-y lang_dropdown-mw scalable-languages-container">
<th:block th:insert="~{fragments/languages :: langs}"></th:block>
</div>
@ -157,15 +160,16 @@
search
</span>
<span class="icon-text icon-hide">Search</span>
<span class="material-symbols-rounded chevron-icon">expand_more</span>
</a>
<div class="dropdown-menu dropdown-menu-tp" aria-labelledby="searchDropdown">
<div class="dropdown-menu-wrapper px-xl-2 px-2">
<div class="dropdown-menu-wrapper px-xl-2 px-2 scroll-lock-y" style="max-width: 95vw !important;">
<form th:action="@{''}" class="d-flex p-2 search-form" id="searchForm">
<input class="form-control search-input" type="search" th:placeholder="#{navbar.search}"
aria-label="Search" id="navbarSearchInput">
</form>
<!-- Search Results -->
<div id="searchResults" class="search-results scrollable-y dropdown-mw-20"></div>
<div id="searchResults" class="search-results scroll-lock-y dropdown-mw-20"></div>
</div>
</div>
</li>

View File

@ -30,10 +30,6 @@ See https://github.com/adobe-type-tools/cmap-resources
<meta name="google" content="notranslate">
<title>PDF.js viewer</title>
<!-- Bootstrap -->
<script th:src="@{'/js/thirdParty/popper.min.js'}"></script>
<script th:src="@{'/js/thirdParty/bootstrap.min.js'}"></script>
<link rel="stylesheet" th:href="@{'/css/theme/componentes.css'}">
<link rel="stylesheet" th:href="@{'/css/navbar.css'}">