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/core/build
/app/common/build /app/common/build
/app/proprietary/build /app/proprietary/build
common/build
proprietary/build
stirling-pdf/build
# Byte-compiled / optimized / DLL files # Byte-compiled / optimized / DLL files
__pycache__/ __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 (Português) (pt_PT) | ![70%](https://geps.dev/progress/70) |
| Portuguese Brazilian (Português) (pt_BR) | ![77%](https://geps.dev/progress/77) | | Portuguese Brazilian (Português) (pt_BR) | ![77%](https://geps.dev/progress/77) |
| Romanian (Română) (ro_RO) | ![59%](https://geps.dev/progress/59) | | 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) | | Serbian Latin alphabet (Srpski) (sr_LATN_RS) | ![97%](https://geps.dev/progress/97) |
| Simplified Chinese (简体中文) (zh_CN) | ![95%](https://geps.dev/progress/95) | | Simplified Chinese (简体中文) (zh_CN) | ![95%](https://geps.dev/progress/95) |
| Slovakian (Slovensky) (sk_SK) | ![53%](https://geps.dev/progress/53) | | 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) // This aspect will run before any audit aspects due to @Order(0)
// Extract parameters from the request and annotation // Extract parameters from the request and annotation
boolean async = Boolean.parseBoolean(request.getParameter("async")); 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(); long timeout = autoJobPostMapping.timeout();
int retryCount = autoJobPostMapping.retryCount(); int retryCount = autoJobPostMapping.retryCount();
boolean trackProgress = autoJobPostMapping.trackProgress(); boolean trackProgress = autoJobPostMapping.trackProgress();
@ -219,10 +223,9 @@ public class AutoJobAspect {
resourceWeight); resourceWeight);
} }
/** /**
* Processes arguments in-place to handle file resolution and async file persistence. * Processes arguments in-place to handle file resolution and async file persistence. This
* This approach avoids type mismatch issues by modifying the original objects directly. * approach avoids type mismatch issues by modifying the original objects directly.
* *
* @param originalArgs The original arguments * @param originalArgs The original arguments
* @param async Whether this is an async operation * @param async Whether this is an async operation

View File

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

View File

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

View File

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

View File

@ -159,41 +159,36 @@
.scalable-languages-container { .scalable-languages-container {
display: grid; display: grid;
/* Auto-fill columns, with a minimum width of 180px */ grid-template-columns: repeat(1, 1fr);
grid-template-columns: repeat(auto-fill, minmax(180px, 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 { .scalable-languages-container:not(:has(> :nth-child(4))) .lang-dropdown-item-wrapper:last-child {
border: 0px !important border: 0px !important
} }
.scalable-languages-container:has(> *:nth-child(1)) { html[dir="ltr"] #languageSelection .lang-dropdown-item-wrapper:last-child {
--count: 1; border-right: none !important;
} }
.scalable-languages-container:has(> *:nth-child(2)) { #languageSelection .lang-dropdown-item-wrapper:last-child {
--count: 2; 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) { @media (max-width: 600px) {
.scalable-languages-container { .scalable-languages-container {
grid-template-columns: repeat(2, 1fr); 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 { .dropdown-item .icon-text {
text-wrap: wrap; text-wrap: wrap;
word-break: break-word; word-break: break-word;
@ -290,6 +320,21 @@ span.icon-text::after {
color: var(--md-sys-color-on-surface-variant); 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 { .nav-item {
position: relative; position: relative;
} }
@ -473,6 +518,7 @@ html[dir="rtl"] .dropdown-menu {
box-shadow: var(--md-sys-elevation-2); box-shadow: var(--md-sys-elevation-2);
} }
.dropdown-menu-tp { .dropdown-menu-tp {
color: transparent; color: transparent;
background-color: transparent; background-color: transparent;
@ -484,27 +530,119 @@ html[dir="rtl"] .dropdown-menu {
display: inline-flex; display: inline-flex;
} }
@media (min-width:992px) { @media (max-width:1199.98px) {
.dropdown:hover .dropdown-menu { .navbar-collapse .dropdown-menu {
display: block; width: 100%;
margin-top: 0;
} }
.navbar-collapse .dropdown-menu-wrapper {
/* .icon-hide { width: 100%;
display: none; box-sizing: border-box;
} */ }
} .navbar-collapse .dropdown-mw-28 {
min-width: 0;
@media (max-width:1199px) {
.icon-hide {
display: inline-flex;
} }
} }
@media (min-width:1200px) { @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 { .icon-hide {
display: none; 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 { .go-pro-link {
@ -558,6 +696,12 @@ html[dir="rtl"] .dropdown-menu {
box-sizing: border-box; box-sizing: border-box;
} }
@media (max-width: 768px) {
.feature-group {
min-width: 10rem;
}
}
.feature-rows { .feature-rows {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;

View File

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

View File

@ -44,8 +44,16 @@ function updateFavoritesDropdown() {
contentWrapper.style.color = 'inherit'; contentWrapper.style.color = 'inherit';
// Clone the original content // Clone the original content
var originalContent = navbarEntry.querySelector('div').cloneNode(true); var divElement = navbarEntry.querySelector('div');
if (divElement) {
var originalContent = divElement.cloneNode(true);
contentWrapper.appendChild(originalContent); 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 // Create the remove button
var removeButton = document.createElement('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 = () => { window.tooltipSetup = () => {
const tooltipElements = document.querySelectorAll('[title]'); const tooltipElements = document.querySelectorAll('[title]');
@ -56,23 +89,54 @@ window.tooltipSetup = () => {
document.body.appendChild(customTooltip); document.body.appendChild(customTooltip);
element.addEventListener('mouseenter', (event) => { element.addEventListener('mouseenter', (event) => {
if (window.innerWidth >= 1200) {
customTooltip.style.display = 'block'; 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`;
});
// 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.left = `${event.pageX + 10}px`;
customTooltip.style.top = `${event.pageY + 10}px`; customTooltip.style.top = `${event.pageY + 10}px`;
}
});
element.addEventListener('mousemove', (event) => {
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', () => { element.addEventListener('mouseleave', () => {
customTooltip.style.display = 'none'; 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', () => { document.addEventListener('DOMContentLoaded', () => {
tooltipSetup(); 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.textDecoration = "none";
contentWrapper.style.color = "inherit"; contentWrapper.style.color = "inherit";
var originalContent = item.querySelector("div").cloneNode(true); var divElement = item.querySelector("div");
if (divElement) {
var originalContent = divElement.cloneNode(true);
contentWrapper.appendChild(originalContent); 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 () { contentWrapper.onclick = function () {
window.location.href = itemHref; window.location.href = itemHref;
@ -77,35 +85,52 @@ document.querySelector("#navbarSearchInput").addEventListener("input", function
const searchDropdown = document.getElementById('searchDropdown'); const searchDropdown = document.getElementById('searchDropdown');
const searchInput = document.getElementById('navbarSearchInput'); const searchInput = document.getElementById('navbarSearchInput');
const dropdownMenu = searchDropdown.querySelector('.dropdown-menu');
// Handle dropdown shown event // Check if elements exist before proceeding
searchDropdown.addEventListener('shown.bs.dropdown', function () { if (searchDropdown && searchInput) {
searchInput.focus(); const dropdownMenu = searchDropdown.querySelector('.dropdown-menu');
});
// Handle hover opening // Create a single dropdown instance
searchDropdown.addEventListener('mouseenter', function () {
const dropdownInstance = new bootstrap.Dropdown(searchDropdown); const dropdownInstance = new bootstrap.Dropdown(searchDropdown);
// 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(); dropdownInstance.show();
setTimeout(() => searchInput.focus(), 150);
setTimeout(() => { } else {
searchInput.focus();
}, 100);
});
// 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(); dropdownInstance.hide();
} }
}); });
searchDropdown.addEventListener('hidden.bs.dropdown', function () { // Hide dropdown if it's open and user clicks outside
if (searchInput.value.trim().length === 0) { document.addEventListener('click', function(e) {
searchInput.blur(); if (!searchDropdown.contains(e.target) && dropdownMenu.classList.contains('show')) {
dropdownInstance.hide();
} }
}); });
// Keep dropdown open if search input is clicked
searchInput.addEventListener('click', function (e) {
e.stopPropagation();
});
}

View File

@ -30,7 +30,39 @@
// Determine if this is actually a high DPI screen at page load // Determine if this is actually a high DPI screen at page load
const isHighDPI = systemDPR > 1.4; 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() { 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 currentDPR = window.devicePixelRatio || 1;
const browserZoom = currentDPR / systemDPR; const browserZoom = currentDPR / systemDPR;

View File

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

View File

@ -30,10 +30,6 @@ See https://github.com/adobe-type-tools/cmap-resources
<meta name="google" content="notranslate"> <meta name="google" content="notranslate">
<title>PDF.js viewer</title> <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/theme/componentes.css'}">
<link rel="stylesheet" th:href="@{'/css/navbar.css'}"> <link rel="stylesheet" th:href="@{'/css/navbar.css'}">