diff --git a/README.md b/README.md
index 844a34fe..a13c83ba 100644
--- a/README.md
+++ b/README.md
@@ -192,7 +192,7 @@ Stirling-PDF currently supports 37 languages!
| Language | Progress |
| -------------------------------------------- | -------------------------------------- |
| Arabic (العربية) (ar_AR) | ![98%](https://geps.dev/progress/98) |
-| Azerbaijani (Azərbaycan Dili) (az_AZ) | ![76%](https://geps.dev/progress/76) |
+| Azerbaijani (Azərbaycan Dili) (az_AZ) | ![97%](https://geps.dev/progress/97) |
| Basque (Euskara) (eu_ES) | ![54%](https://geps.dev/progress/54) |
| Bulgarian (Български) (bg_BG) | ![94%](https://geps.dev/progress/94) |
| Catalan (Català) (ca_CA) | ![88%](https://geps.dev/progress/88) |
@@ -206,23 +206,23 @@ Stirling-PDF currently supports 37 languages!
| German (Deutsch) (de_DE) | ![97%](https://geps.dev/progress/97) |
| Greek (Ελληνικά) (el_GR) | ![95%](https://geps.dev/progress/95) |
| Hindi (हिंदी) (hi_IN) | ![93%](https://geps.dev/progress/93) |
-| Hungarian (Magyar) (hu_HU) | ![96%](https://geps.dev/progress/96) |
+| Hungarian (Magyar) (hu_HU) | ![95%](https://geps.dev/progress/95) |
| Indonesian (Bahasa Indonesia) (id_ID) | ![95%](https://geps.dev/progress/95) |
| Irish (Gaeilge) (ga_IE) | ![86%](https://geps.dev/progress/86) |
-| Italian (Italiano) (it_IT) | ![98%](https://geps.dev/progress/98) |
+| Italian (Italiano) (it_IT) | ![99%](https://geps.dev/progress/99) |
| Japanese (日本語) (ja_JP) | ![83%](https://geps.dev/progress/83) |
| Korean (한국어) (ko_KR) | ![93%](https://geps.dev/progress/93) |
-| Norwegian (Norsk) (no_NB) | ![86%](https://geps.dev/progress/86) |
-| Polish (Polski) (pl_PL) | ![95%](https://geps.dev/progress/95) |
+| Norwegian (Norsk) (no_NB) | ![85%](https://geps.dev/progress/85) |
+| Polish (Polski) (pl_PL) | ![94%](https://geps.dev/progress/94) |
| Portuguese (Português) (pt_PT) | ![95%](https://geps.dev/progress/95) |
| Portuguese Brazilian (Português) (pt_BR) | ![96%](https://geps.dev/progress/96) |
| Romanian (Română) (ro_RO) | ![88%](https://geps.dev/progress/88) |
| Russian (Русский) (ru_RU) | ![95%](https://geps.dev/progress/95) |
-| Serbian Latin alphabet (Srpski) (sr_LATN_RS) | ![69%](https://geps.dev/progress/69) |
+| Serbian Latin alphabet (Srpski) (sr_LATN_RS) | ![68%](https://geps.dev/progress/68) |
| Simplified Chinese (简体中文) (zh_CN) | ![89%](https://geps.dev/progress/89) |
| Slovakian (Slovensky) (sk_SK) | ![80%](https://geps.dev/progress/80) |
| Spanish (Español) (es_ES) | ![96%](https://geps.dev/progress/96) |
-| Swedish (Svenska) (sv_SE) | ![95%](https://geps.dev/progress/95) |
+| Swedish (Svenska) (sv_SE) | ![94%](https://geps.dev/progress/94) |
| Thai (ไทย) (th_TH) | ![94%](https://geps.dev/progress/94) |
| Traditional Chinese (繁體中文) (zh_TW) | ![96%](https://geps.dev/progress/96) |
| Turkish (Türkçe) (tr_TR) | ![90%](https://geps.dev/progress/90) |
diff --git a/src/main/resources/messages_ar_AR.properties b/src/main/resources/messages_ar_AR.properties
index aff70807..1202616b 100644
--- a/src/main/resources/messages_ar_AR.properties
+++ b/src/main/resources/messages_ar_AR.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=تحريك إلى اليسار
multiTool.moveRight=تحريك إلى اليمين
multiTool.delete=حذف
multiTool.dragDropMessage=الصفحات المحددة
+multiTool.undo=تراجع
+multiTool.redo=إعادة إجراء
#multiTool-advert
multiTool-advert.message=هذه الميزة متوفرة في صفحة الأدوات المتعددة لدينا. اطلع عليها للحصول على واجهة مستخدم محسّنة لكل صفحة وميزات إضافية!
diff --git a/src/main/resources/messages_az_AZ.properties b/src/main/resources/messages_az_AZ.properties
index 4b2fcf55..2aca8d4d 100644
--- a/src/main/resources/messages_az_AZ.properties
+++ b/src/main/resources/messages_az_AZ.properties
@@ -34,10 +34,10 @@ sizes.small=Kiçik
sizes.medium=Orta
sizes.large=Böyük
sizes.x-large=Ekstra Böyük
-error.pdfPassword=PDF sənədi şifrələnmişdir və şifrə təmin edilməmişdir və ya yanlışdır.
+error.pdfPassword=PDF sənədi şifrlənmişdir və şifr təmin edilməmişdir və ya yanlışdır.
delete=Sil
username=İstifadəçi Adı
-password=Şifrə
+password=Şifr
welcome=Xoş gəldiniz
property=Xüsusiyyət
black=Qara
@@ -53,11 +53,11 @@ no=Xeyr
changedCredsMessage=Etibarnamələr dəyişdirildi!
notAuthenticatedMessage=İstifadəçinin kimliyi təsdiqlənməyib.
userNotFoundMessage=İstifadəçi tapılmadı.
-incorrectPasswordMessage=Cari şifrə yanlışdır.
+incorrectPasswordMessage=Cari şifr yanlışdır.
usernameExistsMessage=İstifadəçi adı mövcuddur.
invalidUsernameMessage=Yanlış istifadəçi adı, istifadəçi adı sadəcə hərflərdən, rəqəmlərdən və @._+- xüsusi simvollarından ibarət ola bilər və ya düzgün email ünvanı olmalıdır.
-invalidPasswordMessage=Şifrə boş olmamalıdır, başlanğıc və sonunda boşluqdan istifadə edilməməlidir.
-confirmPasswordErrorMessage=Yeni Şifrə və Yeni Şifrəni Doğrula uyğun olmalıdır.
+invalidPasswordMessage=Şifr boş olmamalıdır, başlanğıc və sonunda boşluqdan istifadə edilməməlidir.
+confirmPasswordErrorMessage=Yeni Şifr və Yeni Şifri Doğrula uyğun olmalıdır.
deleteCurrentUserMessage=Hazırda daxil olmuş istifadəçini silmək mümkün deyil.
deleteUsernameExistsMessage=İstifadəçi adı mövcud deyildir və silinə bilməz.
downgradeCurrentUserMessage=Cari istifadəçinin rolunu aşağı salmaq mümkün deyil
@@ -70,8 +70,8 @@ oops=Oops!
help=Yardım
goHomepage=Ana səhifəyə get
joinDiscord=Discord serverimizə qatıl
-seeDockerHub=Docker Hub'a bax
-visitGithub=Github Repository'ə Baş Çək
+seeDockerHub=Docker Hub-a bax
+visitGithub=Github Repository-ə Baş Çək
donate=İanə Ver
color=Rəng
sponsor=Sponsor
@@ -126,9 +126,9 @@ enterpriseEdition.ssoAdvert=Daha çox istifadəçi-idarəetmə xüsusiyyətləri
#################
# Analytics #
#################
-analytics.title=Stirling PDF'i daha yaxşı etmək istəyirsinizmi?
+analytics.title=Stirling PDF-i daha yaxşı etmək istəyirsinizmi?
analytics.paragraph1=Stirling PDF bizə məhsulu inkişaf etdirməyə kömək etmək üçün analitikaya üstünlük verib. Biz heç bir şəxsi məlumatı və ya fayl məzmununu izləmirik.
-analytics.paragraph2=Zəhmət olmasa, Stringling-PDF'ə inkişaf etməkdə və istifadəçilərimizi daha yaxşı anlamaqda yardım etmək üçün analitikanı aktivləşdirməyi nəzərə alın.
+analytics.paragraph2=Zəhmət olmasa, Stringling-PDF-ə inkişaf etməkdə və istifadəçilərimizi daha yaxşı anlamaqda yardım etmək üçün analitikanı aktivləşdirməyi nəzərə alın.
analytics.enable=Analitikanı aktivləşdir
analytics.disable=Analitikanı deaktivləşdir
analytics.settings=Analitikanın parametrlərini config/settings.yml faylından dəyişə bilərsiniz.
@@ -141,11 +141,11 @@ navbar.darkmode=Qaranlıq Tema
navbar.language=Dillər
navbar.settings=Parametrlər
navbar.allTools=Alətlər
-navbar.multiTool=Çox Alət
+navbar.multiTool=Multi-Alət
navbar.search=Axtar
navbar.sections.organize=Təşkil et
-navbar.sections.convertTo=PDF'ə Çevir
-navbar.sections.convertFrom=PDF'dən Çevir
+navbar.sections.convertTo=PDF-ə Çevir
+navbar.sections.convertFrom=PDF-dən Çevir
navbar.sections.security=İmza & Təhlükəsizlik
navbar.sections.advance=Qabaqcıl
navbar.sections.edit=Bax & Redaktə et
@@ -171,11 +171,11 @@ settings.cacheInputs.help=Gələcək əməliyyatlar üçün əvvəllər istifad
changeCreds.title=Məlumatları dəyişdirin
changeCreds.header=Hesab Məlumatlarınızı Yeniləyin
-changeCreds.changePassword=Siz standart giriş məlumatlarından istifadə edirsiniz. Zəhmət olmasa, yeni şifrə daxil edin
+changeCreds.changePassword=Siz standart giriş məlumatlarından istifadə edirsiniz. Zəhmət olmasa, yeni şifr daxil edin
changeCreds.newUsername=Yeni İstifadəçi Adı
-changeCreds.oldPassword=Cari Şifrə
-changeCreds.newPassword=Yeni Şifrə
-changeCreds.confirmNewPassword=Yeni Şifrəni Təsdiqləyin
+changeCreds.oldPassword=Cari Şifr
+changeCreds.newPassword=Yeni Şifr
+changeCreds.confirmNewPassword=Yeni Şifri Təsdiqləyin
changeCreds.submit=Dəyişiklikləri Təsdiqlə
@@ -186,11 +186,11 @@ account.adminSettings=Admin Paramterləri - İstifadəçilər Əlavə Et və Onl
account.userControlSettings=İstifadəçi İdarəetmə Parametrləri
account.changeUsername=İstifadəçi Adını Dəyiş
account.newUsername=Yeni İstifadəçi Adı
-account.password=Təsdiqləmə Şifrəsi
-account.oldPassword=Keçmiş Şifrə
-account.newPassword=Yeni Şifrə
-account.changePassword=Şifrəni Dəyiş
-account.confirmNewPassword=Yeni Şifrəni Təsdiqlə
+account.password=Təsdiqləmə Şifri
+account.oldPassword=Keçmiş Şifr
+account.newPassword=Yeni Şifr
+account.changePassword=Şifri Dəyiş
+account.confirmNewPassword=Yeni Şifri Təsdiqlə
account.signOut=Çıxış
account.yourApiKey=Sizin API Açarınız
account.syncTitle=Brauzer parametrlərini hesabla sinxronlaşdırın
@@ -543,7 +543,7 @@ login.title=Daxil olun
login.header=Daxil olun
login.signin=Daxil olun
login.rememberme=Məni xatırla
-login.invalid=Etibarsız istifadəçi adı və ya şifrə.
+login.invalid=Etibarsız istifadəçi adı və ya şifr.
login.locked=Sizin hesabınız kilidlənmişdir.
login.signinTitle=Zəhmət olmasa, daxil olun
login.ssoSignIn=Single Sign-on vasitəsilə daxil olun
@@ -581,8 +581,8 @@ showJS.submit=Göstər
#pdfToSinglePage
-pdfToSinglePage.title=PDF'dən Tək Səhifəyə
-pdfToSinglePage.header=PDF'dən Tək Səhifəyə
+pdfToSinglePage.title=PDF-dən Tək Səhifəyə
+pdfToSinglePage.header=PDF-dən Tək Səhifəyə
pdfToSinglePage.submit=Tək Səhifəyə Çevir
@@ -601,8 +601,8 @@ getPdfInfo.downloadJson=JSON yüklə
#markdown-to-pdf
-MarkdownToPDF.title=Markdown'dan PDF'ə
-MarkdownToPDF.header=Markdown'dan PDF'ə
+MarkdownToPDF.title=Markdown-dan PDF-ə
+MarkdownToPDF.header=Markdown-dan PDF-ə
MarkdownToPDF.submit=Çevir
MarkdownToPDF.help=İş davam edir
MarkdownToPDF.credit=WeasyPrint İstifadə Edir
@@ -617,8 +617,8 @@ URLToPDF.credit=WeasyPrint İstifadə Edir
#html-to-pdf
-HTMLToPDF.title=HTML'dən PDF'ə
-HTMLToPDF.header=HTML'dən PDF'ə
+HTMLToPDF.title=HTML-dən PDF-ə
+HTMLToPDF.header=HTML-dən PDF-ə
HTMLToPDF.help=HTML fayllarını və tərkibində mütləq html/css/images və s. olan ZIP fayllarını qəbul edir
HTMLToPDF.submit=Çevir
HTMLToPDF.credit=WeasyPrint İstifadə Edir
@@ -656,14 +656,14 @@ AddStampRequest.submit=Təsdiqlə
#sanitizePDF
-sanitizePDF.title=PDF'i Təmizlə
+sanitizePDF.title=PDF-i Təmizlə
sanitizePDF.header=PDF Faylını Təmizlə
sanitizePDF.selectText.1=JavaScript Fəaliyyətlərini Sil
sanitizePDF.selectText.2=Daxil Edilmiş Faylları Sil
sanitizePDF.selectText.3=Metadatanı Sil
sanitizePDF.selectText.4=Linkləri Sil
sanitizePDF.selectText.5=Şriftləri Sil
-sanitizePDF.submit=PDF'i Təmizlə
+sanitizePDF.submit=PDF-i Təmizlə
#addPageNumbers
@@ -683,7 +683,7 @@ addPageNumbers.submit=Səhifə Nömrələri əlavə edin
#auto-rename
auto-rename.title=Avtomatik Yenidən Adlandır
-auto-rename.header=Pdf'in Adını Avtomatik Yenidən Adlandır
+auto-rename.header=Pdf-in Adını Avtomatik Yenidən Adlandır
auto-rename.submit=Avtomatik Yenidən Adlandır
@@ -698,7 +698,7 @@ adjustContrast.download=Yüklə
#crop
crop.title=Kəs
-crop.header=Pdf'ləri Kəs
+crop.header=Pdf-ləri Kəs
crop.submit=Təsdiq Et
@@ -722,8 +722,8 @@ pipeline.title=Pipeline
#pageLayout
-pageLayout.title=Çoxsəhifəli Sxem
-pageLayout.header=Çoxsəhifəli Sxem
+pageLayout.title=Çoxsəhifəli Tərtibat
+pageLayout.header=Çoxsəhifəli Tərtibat
pageLayout.pagesPerSheet=Vərəqdəki Səhifə Sayı:
pageLayout.addBorder=Çərçivə Əlavə Et
pageLayout.submit=Təsdiq et
@@ -739,22 +739,22 @@ scalePages.submit=Təsdiq edin
#certSign
-certSign.title=Certificate Signing
-certSign.header=Sign a PDF with your certificate (Work in progress)
-certSign.selectPDF=Select a PDF File for Signing:
-certSign.jksNote=Note: If your certificate type is not listed below, please convert it to a Java Keystore (.jks) file using the keytool command line tool. Then, choose the .jks file option below.
-certSign.selectKey=Select Your Private Key File (PKCS#8 format, could be .pem or .der):
-certSign.selectCert=Select Your Certificate File (X.509 format, could be .pem or .der):
-certSign.selectP12=Select Your PKCS#12 Keystore File (.p12 or .pfx) (Optional, If provided, it should contain your private key and certificate):
-certSign.selectJKS=Select Your Java Keystore File (.jks or .keystore):
-certSign.certType=Certificate Type
-certSign.password=Enter Your Keystore or Private Key Password (If Any):
-certSign.showSig=Show Signature
-certSign.reason=Reason
-certSign.location=Location
-certSign.name=Name
-certSign.showLogo=Show Logo
-certSign.submit=Sign PDF
+certSign.title=Sertifikatla İmzala
+certSign.header=PDF-i Sertifikatınızla İmzalayın (İşlənilir)
+certSign.selectPDF=İmzalamaq üçün PDF Faylı seçin:
+certSign.jksNote=Note: Əgər sertifikatınızın tipi aşağıda göstərilməyibsə, zəhmət olmasa "Keytool command line tool" istifadə edərək onu "Java Keystroke" (.jks) faylına çevirin. Sonra, aşağıdan .jks faylını seçin.
+certSign.selectKey=Şəxsi Açar faylınızı seçin (PKCS#8 format, .pem və ya .der ola bilər):
+certSign.selectCert=Sertifikat faylınızı seçin (X.509 format, .pem və ya .der ola bilər):
+certSign.selectP12=PKCS#12 Keystore Faylınızı seçin (.p12 və ya .pfx) (İstəyə bağlı, əgər təmin olunarsa, şəxsi açar və sertifikatınızı ehtiva etməlidir):
+certSign.selectJKS=Java Keystore Faylınızı seçin (.jks və ya .keystore):
+certSign.certType=Sertifikat Tipi
+certSign.password=Keystore və ya Şəxsi Açar daxil edin (Əgər varsa):
+certSign.showSig=İmzanı Göstər
+certSign.reason=Səbəb
+certSign.location=Məkan
+certSign.name=Ad
+certSign.showLogo=Loqonu Göstər
+certSign.submit=PDF-i İmzala
#removeCertSign
@@ -765,13 +765,13 @@ removeCertSign.submit=İmzanı silin
#removeBlanks
-removeBlanks.title=Remove Blanks
-removeBlanks.header=Remove Blank Pages
-removeBlanks.threshold=Pixel Whiteness Threshold:
-removeBlanks.thresholdDesc=Threshold for determining how white a white pixel must be to be classed as 'White'. 0 = Black, 255 pure white.
-removeBlanks.whitePercent=White Percent (%):
-removeBlanks.whitePercentDesc=Percent of page that must be 'white' pixels to be removed
-removeBlanks.submit=Remove Blanks
+removeBlanks.title=Boş Səhifələri Sil
+removeBlanks.header=Boş SƏhifələri Silir
+removeBlanks.threshold=Minimal Piksel Bəyazlığı:
+removeBlanks.thresholdDesc=Pikselin "Ağ" hesab olunması üçün minimal nə qədər bəyaz olmalı olduğunu təyin edin. 0 = Qara, 255 Ağappaq.
+removeBlanks.whitePercent=Bəyaz Faizi (%):
+removeBlanks.whitePercentDesc=Silinmək üçün səhifənin neçə faizi "ağ" piksellərdən təşkil olunmalıdır
+removeBlanks.submit=Boş Səhifələri Sil
#removeAnnotations
@@ -781,16 +781,16 @@ removeAnnotations.submit=Sil
#compare
-compare.title=Compare
-compare.header=Compare PDFs
-compare.highlightColor.1=Highlight Color 1:
-compare.highlightColor.2=Highlight Color 2:
-compare.document.1=Document 1
-compare.document.2=Document 2
-compare.submit=Compare
-compare.complex.message=One or both of the provided documents are large files, accuracy of comparison may be reduced
-compare.large.file.message=One or Both of the provided documents are too large to process
-compare.no.text.message=One or both of the selected PDFs have no text content. Please choose PDFs with text for comparison.
+compare.title=Müqayisə Et
+compare.header=PDF-ləri Müqayisə Et
+compare.highlightColor.1=Önə Çıxarma Rəngi 1:
+compare.highlightColor.2=Önə Çıxarma Rəngi 2:
+compare.document.1=Sənəd 1
+compare.document.2=Sənəd 2
+compare.submit=Müqayisə Et
+compare.complex.message=Fayllardan biri və ya ikisi də böyük fayldır. Müqayisə effektivliyi azala bilər.
+compare.large.file.message=Fayllardan biri və ya ikisi də işləmək üçün çox böyükdür.
+compare.no.text.message=Fayllardan birində və ya ikisində də mətn məzmunu yoxdur. Zəhmət olmasa, müqayisə üçün mətn məzmunlu PDF seçin.
#BookToPDF
BookToPDF.title=Kitabları və Komiksləri PDF-ə
@@ -818,12 +818,12 @@ sign.save=İmzanı yadda Saxla
sign.personalSigs=Şəxsi İmzalar
sign.sharedSigs=Paylaşılan İmzalar
sign.noSavedSigs=Saxlanmış imza tapılmadı
-sign.addToAll=Add to all pages
-sign.delete=Delete
-sign.first=First page
-sign.last=Last page
-sign.next=Next page
-sign.previous=Previous page
+sign.addToAll=Bütün səhiflərə əlavə et
+sign.delete=Sil
+sign.first=İlk səhifə
+sign.last=Son səhifə
+sign.next=Növbəti səhifə
+sign.previous=Əvvəlki səhifə
#repair
repair.title=Bərpa Et
@@ -839,37 +839,37 @@ flatten.submit=Düzləşdirin
#ScannerImageSplit
-ScannerImageSplit.selectText.1=Angle Threshold:
-ScannerImageSplit.selectText.2=Sets the minimum absolute angle required for the image to be rotated (default: 10).
-ScannerImageSplit.selectText.3=Tolerance:
-ScannerImageSplit.selectText.4=Determines the range of color variation around the estimated background color (default: 30).
-ScannerImageSplit.selectText.5=Minimum Area:
-ScannerImageSplit.selectText.6=Sets the minimum area threshold for a photo (default: 10000).
-ScannerImageSplit.selectText.7=Minimum Contour Area:
-ScannerImageSplit.selectText.8=Sets the minimum contour area threshold for a photo
-ScannerImageSplit.selectText.9=Border Size:
-ScannerImageSplit.selectText.10=Sets the size of the border added and removed to prevent white borders in the output (default: 1).
-ScannerImageSplit.info=Python is not installed. It is required to run.
+ScannerImageSplit.selectText.1=Bucaq Aşağı Limiti:
+ScannerImageSplit.selectText.2=Şəklin fırladılması üçün lazım olan minimal mütləq bucağı təyin edir (defolt: 10).
+ScannerImageSplit.selectText.3=Rəng Toleransı:
+ScannerImageSplit.selectText.4=Təxmin olunan arxaplan rənginin ətrafındakı rəng fərqliliyi intervalını təyin edir (defolt: 30).
+ScannerImageSplit.selectText.5=Minimal Sahə:
+ScannerImageSplit.selectText.6=Foto üçün minimal sahənin aşağı limitini təyin edir (defolt: 10000).
+ScannerImageSplit.selectText.7=Minimal Kontur Sahəsi:
+ScannerImageSplit.selectText.8=Fotonun kontur sahəsi üçün minimal aşağı limiti təyin edir
+ScannerImageSplit.selectText.9=Sərhəd Ölçüsü:
+ScannerImageSplit.selectText.10=Faylda ağ sərhədlərin olmasının qarşısını almaq üçün əlavə ediləcək sərhədin ölçüsünü təyin edir (defolt: 1).
+ScannerImageSplit.info=Python yüklənməyib. İşə salmaq üçün Python lazımdır.
#OCR
-ocr.title=OCR / Scan Cleanup
-ocr.header=Cleanup Scans / OCR (Optical Character Recognition)
-ocr.selectText.1=Select languages that are to be detected within the PDF (Ones listed are the ones currently detected):
-ocr.selectText.2=Produce text file containing OCR text alongside the OCR'ed PDF
-ocr.selectText.3=Correct pages were scanned at a skewed angle by rotating them back into place
-ocr.selectText.4=Clean page so its less likely that OCR will find text in background noise. (No output change)
-ocr.selectText.5=Clean page so its less likely that OCR will find text in background noise, maintains cleanup in output.
-ocr.selectText.6=Ignores pages that have interactive text on them, only OCRs pages that are images
-ocr.selectText.7=Force OCR, will OCR Every page removing all original text elements
-ocr.selectText.8=Normal (Will error if PDF contains text)
-ocr.selectText.9=Additional Settings
-ocr.selectText.10=OCR Mode
-ocr.selectText.11=Remove images after OCR (Removes ALL images, only useful if part of conversion step)
-ocr.selectText.12=Render Type (Advanced)
-ocr.help=Please read this documentation on how to use this for other languages and/or use not in docker
-ocr.credit=This service uses qpdf and Tesseract for OCR.
-ocr.submit=Process PDF with OCR
+ocr.title=OST (OCR) / Skan Təmizləmə
+ocr.header=Skanları Təmizlə / OST (Optik Simvol Tanınması)
+ocr.selectText.1=PDF-də aşkar olunacaq dilləri seçin (Göstərilmiş dillər hazırda aşkar olunmuşlardır):
+ocr.selectText.2=OST-lənmiş PDF ilə yanaşı daxilində OST edilmiş mətn olan PDF yaradın
+ocr.selectText.3=Əyri skan olunmuş səhifələri yerinə fırladaraq düzəldin
+ocr.selectText.4=OST-in arxaplandakı artıq mətni aşkar etməsinin qarşısını almaq üçün səhifəni təmizləyin. (Çıxış dəyişmir)
+ocr.selectText.5=OST-in arxaplandakı artıq mətni aşkar etməsinin qarşısını almaq üçün səhifəni təmizləyin, təmizləməni çıxışa verilən faylda saxlayır.
+ocr.selectText.6=Üzərində interaktiv yazı olan səhifələri nəzərə almır, yalnız şəkil olan səhifələri OST edir.
+ocr.selectText.7=OST-ə məcbur et, bütün orijinal mətn elementlərini silərək hər səhifəni OST edir
+ocr.selectText.8=Normal (PDF-də mətn varsa, xəta verəcək)
+ocr.selectText.9=Əlavə Parametrlər
+ocr.selectText.10=OST (OCR) Rejimi
+ocr.selectText.11=OST-dən sonra şəkilləri sil (BÜTÜN şəkilləri silir, ancaq çevirmə prosesinin bir hissəsi olduqda işə yarayır)
+ocr.selectText.12=Render Tipi (Qabaqcıl)
+ocr.help=Bunu digər dillər üçün necə istifadə etmək və/və ya docker-də istifadə etməmək üçün bu dokumentasiyanı oxuyun
+ocr.credit=Bu servis OST (OCR) üçün "OCRmyPDF" və "Tesseract" istifadə edir.
+ocr.submit=PDF-i OST ilə işlə
#extractImages
@@ -890,15 +890,15 @@ fileToPDF.submit=PDF-ə Çevir
#compress
-compress.title=Compress
-compress.header=Compress PDF
-compress.credit=This service uses qpdf for PDF Compress/Optimisation.
-compress.selectText.1=Manual Mode - From 1 to 4
-compress.selectText.2=Optimization level:
-compress.selectText.3=4 (Terrible for text images)
-compress.selectText.4=Auto mode - Auto adjusts quality to get PDF to exact size
-compress.selectText.5=Expected PDF Size (e.g. 25MB, 10.8MB, 25KB)
-compress.submit=Compress
+compress.title=Sıxışdır
+compress.header=PDF-i Sıxışdır
+compress.credit=Bu servis PDF sıxışdırılması/Optimizasiyası üçün Ghostscript istifadə edir.
+compress.selectText.1=Manual Mod - 1-dən 4-ə
+compress.selectText.2=Optimizasiya səviyyəsi:
+compress.selectText.3=4 (Mətn şəkilləri üçün yaxşı deyil)
+compress.selectText.4=Avto mod - PDF-in dəqiq ölçüsünü əldə etmək üçün keyfiyyəti avtomatik tənzimləyir
+compress.selectText.5=Gözlənilən PDF Ölçüsü (məsələn, 25MB, 10.8MB, 25KB)
+compress.submit=Sıxışdır
#Add image
@@ -919,35 +919,35 @@ merge.submit=Birləşdirin
#pdfOrganiser
-pdfOrganiser.title=Page Organiser
-pdfOrganiser.header=PDF Page Organiser
-pdfOrganiser.submit=Rearrange Pages
-pdfOrganiser.mode=Mode
-pdfOrganiser.mode.1=Custom Page Order
-pdfOrganiser.mode.2=Reverse Order
-pdfOrganiser.mode.3=Duplex Sort
-pdfOrganiser.mode.4=Booklet Sort
-pdfOrganiser.mode.5=Side Stitch Booklet Sort
-pdfOrganiser.mode.6=Odd-Even Split
-pdfOrganiser.mode.7=Remove First
-pdfOrganiser.mode.8=Remove Last
-pdfOrganiser.mode.9=Remove First and Last
-pdfOrganiser.mode.10=Odd-Even Merge
-pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
+pdfOrganiser.title=Səhifə Tənzimləyicisi
+pdfOrganiser.header=PDF Səhifə Tənzimləyicisi
+pdfOrganiser.submit=Səhifələri Yenidən Təşkil Edin
+pdfOrganiser.mode=Rejim
+pdfOrganiser.mode.1=Fərdi Səhifə Düzülüşü
+pdfOrganiser.mode.2=Tərs Düzülüş
+pdfOrganiser.mode.3=İkitərəfli Çeşidləmə
+pdfOrganiser.mode.4=Kitabça Çeşidləmə
+pdfOrganiser.mode.5=Yan Tikiş Kitabçasının Çeşidlənməsi
+pdfOrganiser.mode.6=Tək-Cüt Bölünmə
+pdfOrganiser.mode.7=Birincini Sil
+pdfOrganiser.mode.8=Sonuncunu Sil
+pdfOrganiser.mode.9=Birinci və Sonuncunu Sil
+pdfOrganiser.mode.10=Tək-Cüt Birləşdirmə
+pdfOrganiser.placeholder=(məs., 1,3,2 və ya 4-8,2,10-12 və ya 2n-1)
#multiTool
-multiTool.title=PDF Multi Tool
-multiTool.header=PDF Multi Tool
-multiTool.uploadPrompts=File Name
-multiTool.selectAll=Select All
-multiTool.deselectAll=Deselect All
-multiTool.selectPages=Page Select
-multiTool.selectedPages=Selected Pages
-multiTool.page=Page
-multiTool.deleteSelected=Delete Selected
-multiTool.downloadAll=Export
-multiTool.downloadSelected=Export Selected
+multiTool.title=PDF Multi-Alət
+multiTool.header=PDF Multi-Alət
+multiTool.uploadPrompts=Fayl Adı
+multiTool.selectAll=Hamısını Seç
+multiTool.deselectAll=Hamısını Seçməni Ləğv Et
+multiTool.selectPages=Səhifə Seçimi
+multiTool.selectedPages=Seçilmiş Səhifələr
+multiTool.page=Səhifə
+multiTool.deleteSelected=Seçilmişi Sil
+multiTool.downloadAll=İxrac Et
+multiTool.downloadSelected=Seçilmişi İxrac Et
multiTool.insertPageBreak=Insert Page Break
multiTool.addFile=Add File
@@ -957,10 +957,12 @@ multiTool.split=Split
multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
-multiTool.dragDropMessage=Page(s) Selected
+multiTool.dragDropMessage=Seçilmiş Səhifə(lər)
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
-multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features!
+multiTool-advert.message=Bu xüsusiyyət bizim multi-alət səhifəmizdə də mövcuddur. Əlavə xüsusiyyətlər və səhifə-səhifə interfeys üçün sınaqdan keçirin!
#view pdf
viewPdf.title=PDF-ə baxın
@@ -982,32 +984,32 @@ rotate.submit=Fırladın
#split-pdfs
-split.title=Split PDF
-split.header=Split PDF
-split.desc.1=The numbers you select are the page number you wish to do a split on
-split.desc.2=As such selecting 1,3,7-9 would split a 10 page document into 6 separate PDFS with:
-split.desc.3=Document #1: Page 1
-split.desc.4=Document #2: Page 2 and 3
-split.desc.5=Document #3: Page 4, 5, 6 and 7
-split.desc.6=Document #4: Page 8
-split.desc.7=Document #5: Page 9
-split.desc.8=Document #6: Page 10
-split.splitPages=Enter pages to split on:
-split.submit=Split
+split.title=PDF-i Bölün
+split.header=PDF-i Bölün
+split.desc.1=Seçdiyiniz Nömrələr Bölmək İstədiyiniz Səhifə Nömrəsidir
+split.desc.2=Beləliklə, 1,3,7-9 Seçimi 10 Səhifəlik Sənədi 6 Ayrı PDF-ə Böləcək:
+split.desc.3=Sənəd #1: Səhifə 1
+split.desc.4=Sənəd #2: Səhifə 2 və 3
+split.desc.5=Sənəd #3: Səhifə 4, 5, 6 və 7
+split.desc.6=Sənəd #4: Səhifə 8
+split.desc.7=Sənəd #5: Səhifə 9
+split.desc.8=Sənəd #6: Səhifə 10
+split.splitPages=Bölünəcək Səhifələri Daxil Edin:
+split.submit=Bölün
#merge
-imageToPDF.title=Image to PDF
-imageToPDF.header=Image to PDF
-imageToPDF.submit=Convert
-imageToPDF.selectLabel=Image Fit Options
-imageToPDF.fillPage=Fill Page
-imageToPDF.fitDocumentToImage=Fit Page to Image
-imageToPDF.maintainAspectRatio=Maintain Aspect Ratios
-imageToPDF.selectText.2=Auto rotate PDF
-imageToPDF.selectText.3=Multi file logic (Only enabled if working with multiple images)
-imageToPDF.selectText.4=Merge into single PDF
-imageToPDF.selectText.5=Convert to separate PDFs
+imageToPDF.title=Şəkli PDF-ə
+imageToPDF.header=Şəkli PDF-ə
+imageToPDF.submit=Çevir
+imageToPDF.selectLabel=Şəkil Uyğunluğu Seçimləri
+imageToPDF.fillPage=Səhifəni Doldur
+imageToPDF.fitDocumentToImage=Şəklə Uyğun Səhifə
+imageToPDF.maintainAspectRatio=Aspekt Nisbətlərini Qoruyun
+imageToPDF.selectText.2=PDF-i Avtomatik Fırlat
+imageToPDF.selectText.3=Çoxsaylı Fayl Məntiqi (Yalnız Birdən Çox Şəkil İlə İşləyərkən Aktivdir)
+imageToPDF.selectText.4=Tək Bir PDF-ə Birləşdir
+imageToPDF.selectText.5=Ayrı PDF-lərə Çevirin
#pdfToImage
@@ -1026,97 +1028,97 @@ pdfToImage.info=Python Yüklü Deyil.WebP Çevirməsi Üçün Vacibdir
#addPassword
-addPassword.title=Add Password
-addPassword.header=Add password (Encrypt)
-addPassword.selectText.1=Select PDF to encrypt
-addPassword.selectText.2=User Password
-addPassword.selectText.3=Encryption Key Length
-addPassword.selectText.4=Higher values are stronger, but lower values have better compatibility.
-addPassword.selectText.5=Permissions to set (Recommended to be used along with Owner password)
-addPassword.selectText.6=Prevent assembly of document
-addPassword.selectText.7=Prevent content extraction
-addPassword.selectText.8=Prevent extraction for accessibility
-addPassword.selectText.9=Prevent filling in form
-addPassword.selectText.10=Prevent modification
-addPassword.selectText.11=Prevent annotation modification
-addPassword.selectText.12=Prevent printing
-addPassword.selectText.13=Prevent printing different formats
-addPassword.selectText.14=Owner Password
-addPassword.selectText.15=Restricts what can be done with the document once it is opened (Not supported by all readers)
-addPassword.selectText.16=Restricts the opening of the document itself
-addPassword.submit=Encrypt
+addPassword.title=Şifr Əlavə Et
+addPassword.header=Şifr Əlavə Et (Şifrləmə)
+addPassword.selectText.1=Şifrlənəcək PDF-i seç
+addPassword.selectText.2=İstifadəçi Şifri
+addPassword.selectText.3=Şifrləmə Açarı Uzunluğu
+addPassword.selectText.4=Böyük dəyərlər daha güclüdür, lakin kiçik dəyərlərin uyğunluğu yüksəkdir.
+addPassword.selectText.5=Təyin olunacaq icazə (Sahib (Owner) Şifri ilə birgə istifadə olunması tövsiyə olunur.)
+addPassword.selectText.6=Sənədin strukturunun dəyişilməsinin qarşısını al
+addPassword.selectText.7=Məzmun xaric edilməsinin qarşısını al
+addPassword.selectText.8=Əlçatanlıq üçün xaricetmənin qarşısını al
+addPassword.selectText.9=Anketin doldurulmasının qarşısını al
+addPassword.selectText.10=Modifikasiyanın qarşısını al
+addPassword.selectText.11=Sitat modifikasiyasının qarşısını al
+addPassword.selectText.12=Çap etmənin qarşısını al
+addPassword.selectText.13=Müxtəlif formatların çap edilməsinin qarşısını al
+addPassword.selectText.14=Sahib Şifri
+addPassword.selectText.15=Sənəd açıldıqdan sonra onunla nə edilə biləcəyini limitləndir (Bütün oxuyucular dəstəkləmir)
+addPassword.selectText.16=Sənədin özünün açılmağını limitləndirir
+addPassword.submit=Şifrlə
#watermark
-watermark.title=Add Watermark
-watermark.header=Add Watermark
-watermark.selectText.1=Select PDF to add watermark to:
-watermark.selectText.2=Watermark Text:
-watermark.selectText.3=Font Size:
-watermark.selectText.4=Rotation (0-360):
-watermark.selectText.5=widthSpacer (Space between each watermark horizontally):
-watermark.selectText.6=heightSpacer (Space between each watermark vertically):
-watermark.selectText.7=Opacity (0% - 100%):
-watermark.selectText.8=Watermark Type:
-watermark.selectText.9=Watermark Image:
-watermark.selectText.10=Convert PDF to PDF-Image
-watermark.submit=Add Watermark
-watermark.type.1=Text
-watermark.type.2=Image
+watermark.title=Watermark Əlavə Et
+watermark.header=Watermark Əlavə Et
+watermark.selectText.1=Watermark əlavə olunacaq PDF-i seç
+watermark.selectText.2=Watermark Mətni:
+watermark.selectText.3=Şrift Ölçüsü:
+watermark.selectText.4=Fırlatma (0-360):
+watermark.selectText.5=enBoşluq (Üfuqi olaraq watermark-lar arasındakı məsafə):
+watermark.selectText.6=uzunluqBoşluq (Şaquli olaraq watermark-lar arasındakı məsafə):
+watermark.selectText.7=Şəffaflıq (0% - 100%):
+watermark.selectText.8=Watermark Tipi:
+watermark.selectText.9=Watermark Şəkili:
+watermark.selectText.10=PDF-i PDF-Şəkil-ə çevir
+watermark.submit=Watermark Əlavə Et
+watermark.type.1=Mətn
+watermark.type.2=Şəkil
#Change permissions
-permissions.title=Change Permissions
-permissions.header=Change Permissions
-permissions.warning=Warning to have these permissions be unchangeable it is recommended to set them with a password via the add-password page
-permissions.selectText.1=Select PDF to change permissions
-permissions.selectText.2=Permissions to set
-permissions.selectText.3=Prevent assembly of document
-permissions.selectText.4=Prevent content extraction
-permissions.selectText.5=Prevent extraction for accessibility
-permissions.selectText.6=Prevent filling in form
-permissions.selectText.7=Prevent modification
-permissions.selectText.8=Prevent annotation modification
-permissions.selectText.9=Prevent printing
-permissions.selectText.10=Prevent printing different formats
-permissions.submit=Change
+permissions.title=İcazələri Dəyişdir
+permissions.header=İcazələri Dəyişdir
+permissions.warning=Bu İcazələrin Dəyişməz Olması İlə Bağlı Xəbərdarlıq Edərək, Onları Parol Əlavə Et Səhifəsi Vasitəsilə Parolla Təyin Etmək Tövsiyə Olunur.
+permissions.selectText.1=İcazələri Dəyişdirmək Üçün PDF-i Seç
+permissions.selectText.2=Tənzimlənmiş İcazələr
+permissions.selectText.3=Sənədin Yığılmasının Qarşısını Al
+permissions.selectText.4=Məzmunun Çıxarılmasının Qarşısını Al
+permissions.selectText.5=Əlçatanlıq Üçün Çıxarılmasının Qarşısını Alın
+permissions.selectText.6=Formanın Doldurulmasının Qarşısını Alır
+permissions.selectText.7=Modifikasiyanın Qarşısını Al
+permissions.selectText.8=Annotasiyanın Dəyişdirilməsinin Qarşısını Almaq
+permissions.selectText.9=Çapın Qarşısını Al
+permissions.selectText.10=Fərqli Formatlarda Çapın Qarşısını Al
+permissions.submit=Dəyiş
#remove password
-removePassword.title=Remove password
-removePassword.header=Remove password (Decrypt)
-removePassword.selectText.1=Select PDF to Decrypt
-removePassword.selectText.2=Password
-removePassword.submit=Remove
+removePassword.title=Şifri Sil
+removePassword.header=Şifri Sil (Deşifr)
+removePassword.selectText.1=Deşifr Üçün PDF-i Seç
+removePassword.selectText.2=Şifr
+removePassword.submit=Sil
#changeMetadata
-changeMetadata.title=Change Metadata
-changeMetadata.header=Change Metadata
-changeMetadata.selectText.1=Please edit the variables you wish to change
-changeMetadata.selectText.2=Delete all metadata
-changeMetadata.selectText.3=Show Custom Metadata:
-changeMetadata.author=Author:
-changeMetadata.creationDate=Creation Date (yyyy/MM/dd HH:mm:ss):
-changeMetadata.creator=Creator:
-changeMetadata.keywords=Keywords:
-changeMetadata.modDate=Modification Date (yyyy/MM/dd HH:mm:ss):
-changeMetadata.producer=Producer:
-changeMetadata.subject=Subject:
-changeMetadata.trapped=Trapped:
-changeMetadata.selectText.4=Other Metadata:
-changeMetadata.selectText.5=Add Custom Metadata Entry
-changeMetadata.submit=Change
+changeMetadata.title=Metadata-nı Dəyiş
+changeMetadata.header=Metadata-nı Dəyiş
+changeMetadata.selectText.1=Dəyişmək istədiyiniz dəyişənləri redaktə edin
+changeMetadata.selectText.2=Bütün Metadata-nı Sil
+changeMetadata.selectText.3=Fərdi Metadatanı göstərin:
+changeMetadata.author=Müəllif:
+changeMetadata.creationDate=Yaradılma Tarixi (yyyy/MM/dd HH:mm:ss):
+changeMetadata.creator=Yaradıcı:
+changeMetadata.keywords=Açar Sözlər:
+changeMetadata.modDate=Dəyişiklik Tarixi (yyyy/MM/dd HH:mm:ss):
+changeMetadata.producer=İstehsalçı:
+changeMetadata.subject=Mövzu:
+changeMetadata.trapped=Tələ:
+changeMetadata.selectText.4=Digər Metadata:
+changeMetadata.selectText.5=Xüsusi Metadata girişi əlavə edin
+changeMetadata.submit=Dəyiş
#pdfToPDFA
-pdfToPDFA.title=PDF To PDF/A
-pdfToPDFA.header=PDF To PDF/A
-pdfToPDFA.credit=This service uses qpdf for PDF/A conversion
-pdfToPDFA.submit=Convert
-pdfToPDFA.tip=Currently does not work for multiple inputs at once
-pdfToPDFA.outputFormat=Output format
-pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
+pdfToPDFA.title=PDF-i PDF/A-ya
+pdfToPDFA.header=PDF-i PDF/A-ya
+pdfToPDFA.credit=Bu Servis PDF/A Çevirmək Üçün ghostscript İşlədir
+pdfToPDFA.submit=Çevir
+pdfToPDFA.tip=Hazırda Birdən Çox Giriş Üçün İşləmir
+pdfToPDFA.outputFormat=Çıxış Formatı
+pdfToPDFA.pdfWithDigitalSignature=PDF Rəqəmsal İmza Ehtiva Edir.Bu, növbəti addımda silinəcək.
#PDFToWord
@@ -1263,8 +1265,8 @@ splitByChapters.submit=PDF-i Ayır
#release notes
-releases.footer=Releases
-releases.title=Release Notes
-releases.header=Release Notes
-releases.current.version=Current Release
-releases.note=Release notes are only available in English
+releases.footer=Buraxılışlar
+releases.title=Buraxılış Qeydləri
+releases.header=Buraxılış Qeydləri
+releases.current.version=Hazırki Buraxılış
+releases.note=Buraxılış Qeydləri yalnız ingiliscə mövcuddur
diff --git a/src/main/resources/messages_bg_BG.properties b/src/main/resources/messages_bg_BG.properties
index 9e2d727b..f1e5fc3b 100644
--- a/src/main/resources/messages_bg_BG.properties
+++ b/src/main/resources/messages_bg_BG.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features!
diff --git a/src/main/resources/messages_ca_CA.properties b/src/main/resources/messages_ca_CA.properties
index 38ae93a9..aa860cb7 100644
--- a/src/main/resources/messages_ca_CA.properties
+++ b/src/main/resources/messages_ca_CA.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features!
diff --git a/src/main/resources/messages_cs_CZ.properties b/src/main/resources/messages_cs_CZ.properties
index 10c5ce88..3c50d387 100644
--- a/src/main/resources/messages_cs_CZ.properties
+++ b/src/main/resources/messages_cs_CZ.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features!
diff --git a/src/main/resources/messages_da_DK.properties b/src/main/resources/messages_da_DK.properties
index 86e4b3cc..ddaf3c60 100644
--- a/src/main/resources/messages_da_DK.properties
+++ b/src/main/resources/messages_da_DK.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features!
diff --git a/src/main/resources/messages_de_DE.properties b/src/main/resources/messages_de_DE.properties
index 1c9609c5..087b7be7 100644
--- a/src/main/resources/messages_de_DE.properties
+++ b/src/main/resources/messages_de_DE.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=Diese Funktion ist auch auf unserer PDF-Multitool-Seite verfügbar. Probieren Sie sie aus, denn sie bietet eine verbesserte Benutzeroberfläche und zusätzliche Funktionen!
diff --git a/src/main/resources/messages_el_GR.properties b/src/main/resources/messages_el_GR.properties
index b6be4be1..75720377 100644
--- a/src/main/resources/messages_el_GR.properties
+++ b/src/main/resources/messages_el_GR.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features!
diff --git a/src/main/resources/messages_en_GB.properties b/src/main/resources/messages_en_GB.properties
index 8a471ade..e2f74fbc 100644
--- a/src/main/resources/messages_en_GB.properties
+++ b/src/main/resources/messages_en_GB.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features!
diff --git a/src/main/resources/messages_en_US.properties b/src/main/resources/messages_en_US.properties
index cd43508c..cde361d9 100644
--- a/src/main/resources/messages_en_US.properties
+++ b/src/main/resources/messages_en_US.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features!
diff --git a/src/main/resources/messages_es_ES.properties b/src/main/resources/messages_es_ES.properties
index 6869b4f4..db712d42 100644
--- a/src/main/resources/messages_es_ES.properties
+++ b/src/main/resources/messages_es_ES.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features!
diff --git a/src/main/resources/messages_eu_ES.properties b/src/main/resources/messages_eu_ES.properties
index 6022513c..0daedb19 100644
--- a/src/main/resources/messages_eu_ES.properties
+++ b/src/main/resources/messages_eu_ES.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features!
diff --git a/src/main/resources/messages_fr_FR.properties b/src/main/resources/messages_fr_FR.properties
index 9b902ae0..7ac4a8a9 100644
--- a/src/main/resources/messages_fr_FR.properties
+++ b/src/main/resources/messages_fr_FR.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Déplacer vers la gauche
multiTool.moveRight=Déplacer vers la droite
multiTool.delete=Supprimer
multiTool.dragDropMessage=Page(s) sélectionnées
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=Cette fonctionnalité est aussi disponible dans la page de l'outil multifonction. Allez-y pour une interface page par page améliorée et des fonctionnalités additionnelles !
diff --git a/src/main/resources/messages_ga_IE.properties b/src/main/resources/messages_ga_IE.properties
index c1d46c03..8a95af6d 100644
--- a/src/main/resources/messages_ga_IE.properties
+++ b/src/main/resources/messages_ga_IE.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features!
diff --git a/src/main/resources/messages_hi_IN.properties b/src/main/resources/messages_hi_IN.properties
index 490d8bf9..1c61b7ec 100644
--- a/src/main/resources/messages_hi_IN.properties
+++ b/src/main/resources/messages_hi_IN.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features!
diff --git a/src/main/resources/messages_hr_HR.properties b/src/main/resources/messages_hr_HR.properties
index 1e76ba92..efc41e4f 100644
--- a/src/main/resources/messages_hr_HR.properties
+++ b/src/main/resources/messages_hr_HR.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features!
diff --git a/src/main/resources/messages_hu_HU.properties b/src/main/resources/messages_hu_HU.properties
index 4bef0201..841c71fd 100644
--- a/src/main/resources/messages_hu_HU.properties
+++ b/src/main/resources/messages_hu_HU.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features!
diff --git a/src/main/resources/messages_id_ID.properties b/src/main/resources/messages_id_ID.properties
index b04de894..fdd3816b 100644
--- a/src/main/resources/messages_id_ID.properties
+++ b/src/main/resources/messages_id_ID.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features!
diff --git a/src/main/resources/messages_it_IT.properties b/src/main/resources/messages_it_IT.properties
index a34a050b..e9f09ee0 100644
--- a/src/main/resources/messages_it_IT.properties
+++ b/src/main/resources/messages_it_IT.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Sposta a sinistra
multiTool.moveRight=Sposta a destra
multiTool.delete=Elimina
multiTool.dragDropMessage=Pagina(e) selezionata(e)
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=Questa funzione è disponibile anche nella nostra pagina multi-strumento. Scoprila per un'interfaccia utente pagina per pagina migliorata e funzionalità aggiuntive!
@@ -1263,8 +1265,8 @@ splitByChapters.submit=Dividi PDF
#release notes
-releases.footer=Releases
-releases.title=Release Notes
-releases.header=Release Notes
-releases.current.version=Current Release
-releases.note=Release notes are only available in English
+releases.footer=Rilasci
+releases.title=Note di rilascio
+releases.header=Note di rilascio
+releases.current.version=Rilascio corrente
+releases.note=Le note di rilascio sono disponibili solo in inglese
diff --git a/src/main/resources/messages_ja_JP.properties b/src/main/resources/messages_ja_JP.properties
index 5574521c..e893029b 100644
--- a/src/main/resources/messages_ja_JP.properties
+++ b/src/main/resources/messages_ja_JP.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features!
diff --git a/src/main/resources/messages_ko_KR.properties b/src/main/resources/messages_ko_KR.properties
index 27db7dd6..aa77af63 100644
--- a/src/main/resources/messages_ko_KR.properties
+++ b/src/main/resources/messages_ko_KR.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features!
diff --git a/src/main/resources/messages_nl_NL.properties b/src/main/resources/messages_nl_NL.properties
index b6f0ef0e..23ac465a 100644
--- a/src/main/resources/messages_nl_NL.properties
+++ b/src/main/resources/messages_nl_NL.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features!
diff --git a/src/main/resources/messages_no_NB.properties b/src/main/resources/messages_no_NB.properties
index 0a4461cc..8d1220e6 100644
--- a/src/main/resources/messages_no_NB.properties
+++ b/src/main/resources/messages_no_NB.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features!
diff --git a/src/main/resources/messages_pl_PL.properties b/src/main/resources/messages_pl_PL.properties
index ffc2e33a..02469e1f 100755
--- a/src/main/resources/messages_pl_PL.properties
+++ b/src/main/resources/messages_pl_PL.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features!
diff --git a/src/main/resources/messages_pt_BR.properties b/src/main/resources/messages_pt_BR.properties
index aba81922..b7af46b1 100644
--- a/src/main/resources/messages_pt_BR.properties
+++ b/src/main/resources/messages_pt_BR.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features!
diff --git a/src/main/resources/messages_pt_PT.properties b/src/main/resources/messages_pt_PT.properties
index 0f3830c2..f5f2137b 100644
--- a/src/main/resources/messages_pt_PT.properties
+++ b/src/main/resources/messages_pt_PT.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features!
diff --git a/src/main/resources/messages_ro_RO.properties b/src/main/resources/messages_ro_RO.properties
index d289ff96..6be97127 100644
--- a/src/main/resources/messages_ro_RO.properties
+++ b/src/main/resources/messages_ro_RO.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features!
diff --git a/src/main/resources/messages_ru_RU.properties b/src/main/resources/messages_ru_RU.properties
index 35aefdd0..1908496b 100644
--- a/src/main/resources/messages_ru_RU.properties
+++ b/src/main/resources/messages_ru_RU.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features!
diff --git a/src/main/resources/messages_sk_SK.properties b/src/main/resources/messages_sk_SK.properties
index 0c78dad1..57bb598a 100644
--- a/src/main/resources/messages_sk_SK.properties
+++ b/src/main/resources/messages_sk_SK.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features!
diff --git a/src/main/resources/messages_sr_LATN_RS.properties b/src/main/resources/messages_sr_LATN_RS.properties
index 9ac101ad..34b737d1 100644
--- a/src/main/resources/messages_sr_LATN_RS.properties
+++ b/src/main/resources/messages_sr_LATN_RS.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features!
diff --git a/src/main/resources/messages_sv_SE.properties b/src/main/resources/messages_sv_SE.properties
index c2a7de32..98ccaaa1 100644
--- a/src/main/resources/messages_sv_SE.properties
+++ b/src/main/resources/messages_sv_SE.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features!
diff --git a/src/main/resources/messages_th_TH.properties b/src/main/resources/messages_th_TH.properties
index 1b58842b..08e0281b 100644
--- a/src/main/resources/messages_th_TH.properties
+++ b/src/main/resources/messages_th_TH.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features!
diff --git a/src/main/resources/messages_tr_TR.properties b/src/main/resources/messages_tr_TR.properties
index 79130da2..e7116536 100644
--- a/src/main/resources/messages_tr_TR.properties
+++ b/src/main/resources/messages_tr_TR.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features!
diff --git a/src/main/resources/messages_uk_UA.properties b/src/main/resources/messages_uk_UA.properties
index 4b9cbd26..1c2b60cf 100644
--- a/src/main/resources/messages_uk_UA.properties
+++ b/src/main/resources/messages_uk_UA.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features!
diff --git a/src/main/resources/messages_vi_VN.properties b/src/main/resources/messages_vi_VN.properties
index 14e78b02..bfb7d9c7 100644
--- a/src/main/resources/messages_vi_VN.properties
+++ b/src/main/resources/messages_vi_VN.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features!
diff --git a/src/main/resources/messages_zh_CN.properties b/src/main/resources/messages_zh_CN.properties
index 12f2b69b..dfec712b 100644
--- a/src/main/resources/messages_zh_CN.properties
+++ b/src/main/resources/messages_zh_CN.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features!
diff --git a/src/main/resources/messages_zh_TW.properties b/src/main/resources/messages_zh_TW.properties
index fd3529a7..c1b3929a 100644
--- a/src/main/resources/messages_zh_TW.properties
+++ b/src/main/resources/messages_zh_TW.properties
@@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
multiTool.moveRight=Move Right
multiTool.delete=Delete
multiTool.dragDropMessage=Page(s) Selected
+multiTool.undo=Undo
+multiTool.redo=Redo
#multiTool-advert
multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features!
diff --git a/src/main/resources/static/js/fileInput.js b/src/main/resources/static/js/fileInput.js
index 7c810de0..4553128d 100644
--- a/src/main/resources/static/js/fileInput.js
+++ b/src/main/resources/static/js/fileInput.js
@@ -85,9 +85,9 @@ function setupFileInput(chooser) {
const isDragAndDrop = e.detail?.source == 'drag-drop';
if (element instanceof HTMLInputElement && element.hasAttribute("multiple")) {
- allFiles = isDragAndDrop ? allFiles : [...allFiles, ...element.files];
+ allFiles = isDragAndDrop ? allFiles : [... allFiles, ... element.files];
} else {
- allFiles = isDragAndDrop ? allFiles : [element.files[0]];
+ allFiles = Array.from(isDragAndDrop ? allFiles : [element.files[0]]);
}
if (!isDragAndDrop) {
diff --git a/src/main/resources/static/js/multitool/PdfActionsManager.js b/src/main/resources/static/js/multitool/PdfActionsManager.js
index b83beb5e..17386fd7 100644
--- a/src/main/resources/static/js/multitool/PdfActionsManager.js
+++ b/src/main/resources/static/js/multitool/PdfActionsManager.js
@@ -1,12 +1,20 @@
+import { DeletePageCommand } from "./commands/delete-page.js";
+import { SelectPageCommand } from "./commands/select.js";
+import { SplitFileCommand } from "./commands/split.js";
+import { UndoManager } from "./UndoManager.js";
+
class PdfActionsManager {
pageDirection;
pagesContainer;
static selectedPages = []; // Static property shared across all instances
+ undoManager;
- constructor(id) {
+ constructor(id, undoManager) {
this.pagesContainer = document.getElementById(id);
this.pageDirection = document.documentElement.getAttribute("dir");
+ this.undoManager = undoManager || new UndoManager();
+
var styleElement = document.createElement("link");
styleElement.rel = "stylesheet";
styleElement.href = "css/pdfActions.css";
@@ -27,7 +35,8 @@ class PdfActionsManager {
const sibling = imgContainer.previousSibling;
if (sibling) {
- this.movePageTo(imgContainer, sibling, true);
+ let movePageCommand = this.movePageTo(imgContainer, sibling, true, true);
+ this._pushUndoClearRedo(movePageCommand);
}
}
@@ -35,7 +44,12 @@ class PdfActionsManager {
var imgContainer = this.getPageContainer(e.target);
const sibling = imgContainer.nextSibling;
if (sibling) {
- this.movePageTo(imgContainer, sibling.nextSibling, true);
+ let movePageCommand = this.movePageTo(
+ imgContainer,
+ sibling.nextSibling,
+ true
+ );
+ this._pushUndoClearRedo(movePageCommand);
}
}
@@ -43,30 +57,27 @@ class PdfActionsManager {
var imgContainer = this.getPageContainer(e.target);
const img = imgContainer.querySelector("img");
- this.rotateElement(img, -90);
+ let rotateCommand = this.rotateElement(img, -90);
+ this._pushUndoClearRedo(rotateCommand);
}
rotateCWButtonCallback(e) {
var imgContainer = this.getPageContainer(e.target);
const img = imgContainer.querySelector("img");
- this.rotateElement(img, 90);
+ let rotateCommand = this.rotateElement(img, 90);
+ this._pushUndoClearRedo(rotateCommand);
}
deletePageButtonCallback(e) {
- var imgContainer = this.getPageContainer(e.target);
- this.pagesContainer.removeChild(imgContainer);
- if (this.pagesContainer.childElementCount === 0) {
- const filenameInput = document.getElementById("filename-input");
- const filenameParagraph = document.getElementById("filename");
- const downloadBtn = document.getElementById("export-button");
+ let imgContainer = this.getPageContainer(e.target);
+ let deletePageCommand = new DeletePageCommand(
+ imgContainer,
+ this.pagesContainer
+ );
+ deletePageCommand.execute();
- filenameInput.disabled = true;
- filenameInput.value = "";
- filenameParagraph.innerText = "";
-
- downloadBtn.disabled = true;
- }
+ this._pushUndoClearRedo(deletePageCommand);
}
insertFileButtonCallback(e) {
@@ -81,7 +92,15 @@ class PdfActionsManager {
splitFileButtonCallback(e) {
var imgContainer = this.getPageContainer(e.target);
- imgContainer.classList.toggle("split-before");
+
+ let splitFileCommand = new SplitFileCommand(imgContainer, "split-before");
+ splitFileCommand.execute();
+
+ this._pushUndoClearRedo(splitFileCommand);
+ }
+
+ _pushUndoClearRedo(command) {
+ this.undoManager.pushUndoClearRedo(command);
}
setActions({ movePageTo, addFiles, rotateElement }) {
@@ -159,25 +178,10 @@ class PdfActionsManager {
selectCheckbox.onchange = () => {
const pageNumber = Array.from(div.parentNode.children).indexOf(div) + 1;
- if (selectCheckbox.checked) {
- //adds to array of selected pages
- window.selectedPages.push(pageNumber);
- } else {
- //remove page from selected pages array
- const index = window.selectedPages.indexOf(pageNumber);
- if (index !== -1) {
- window.selectedPages.splice(index, 1);
- }
- }
+ let selectPageCommand = new SelectPageCommand(pageNumber, selectCheckbox);
+ selectPageCommand.execute();
- if (window.selectedPages.length > 0 && !window.selectPage) {
- window.toggleSelectPageVisibility();
- }
- if (window.selectedPages.length == 0 && window.selectPage) {
- window.toggleSelectPageVisibility();
- }
-
- window.updateSelectedPagesDisplay();
+ this._pushUndoClearRedo(selectPageCommand);
};
const insertFileButtonContainer = document.createElement("div");
diff --git a/src/main/resources/static/js/multitool/PdfContainer.js b/src/main/resources/static/js/multitool/PdfContainer.js
index d87fd6cb..b5721dfa 100644
--- a/src/main/resources/static/js/multitool/PdfContainer.js
+++ b/src/main/resources/static/js/multitool/PdfContainer.js
@@ -1,11 +1,18 @@
+import { MovePageUpCommand, MovePageDownCommand } from "./commands/move-page.js";
+import { RemoveSelectedCommand } from "./commands/remove.js";
+import { RotateAllCommand, RotateElementCommand } from "./commands/rotate.js";
+import { SplitAllCommand } from "./commands/split.js";
+import { UndoManager } from "./UndoManager.js";
+
class PdfContainer {
fileName;
pagesContainer;
pagesContainerWrapper;
pdfAdapters;
downloadLink;
+ undoManager;
- constructor(id, wrapperId, pdfAdapters) {
+ constructor(id, wrapperId, pdfAdapters, undoManager) {
this.pagesContainer = document.getElementById(id);
this.pagesContainerWrapper = document.getElementById(wrapperId);
this.downloadLink = null;
@@ -31,6 +38,8 @@ class PdfContainer {
this.removeAllElements = this.removeAllElements.bind(this);
this.resetPages = this.resetPages.bind(this);
+ this.undoManager = undoManager || new UndoManager();
+
this.pdfAdapters = pdfAdapters;
this.pdfAdapters.forEach((adapter) => {
@@ -58,6 +67,33 @@ class PdfContainer {
window.removeAllElements = this.removeAllElements;
window.resetPages = this.resetPages;
+ let undoBtn = document.getElementById('undo-btn');
+ let redoBtn = document.getElementById('redo-btn');
+
+ document.addEventListener('undo-manager-update', (e) => {
+ let canUndo = e.detail.canUndo;
+ let canRedo = e.detail.canRedo;
+
+ undoBtn.disabled = !canUndo;
+ redoBtn.disabled = !canRedo;
+ })
+
+ window.undo = () => {
+ if (undoManager.canUndo()) undoManager.undo();
+ else {
+ undoBtn.disabled = !undoManager.canUndo();
+ redoBtn.disabled = !undoManager.canRedo();
+ }
+ }
+
+ window.redo = () => {
+ if (undoManager.canRedo()) undoManager.redo();
+ else {
+ undoBtn.disabled = !undoManager.canUndo();
+ redoBtn.disabled = !undoManager.canRedo();
+ }
+ }
+
const filenameInput = document.getElementById("filename-input");
const downloadBtn = document.getElementById("export-button");
@@ -68,32 +104,28 @@ class PdfContainer {
downloadBtn.disabled = true;
}
- movePageTo(startElement, endElement, scrollTo = false) {
- const childArray = Array.from(this.pagesContainer.childNodes);
- const startIndex = childArray.indexOf(startElement);
- const endIndex = childArray.indexOf(endElement);
-
- // Check & remove page number elements here too if they exist because Firefox doesn't fire the relevant event on page move.
- const pageNumberElement = startElement.querySelector(".page-number");
- if (pageNumberElement) {
- startElement.removeChild(pageNumberElement);
- }
-
- this.pagesContainer.removeChild(startElement);
- if (!endElement) {
- this.pagesContainer.append(startElement);
+ movePageTo(startElement, endElement, scrollTo = false, moveUp = false) {
+ let movePageCommand;
+ if (moveUp) {
+ movePageCommand = new MovePageUpCommand(
+ startElement,
+ endElement,
+ this.pagesContainer,
+ this.pagesContainerWrapper,
+ scrollTo
+ );
} else {
- this.pagesContainer.insertBefore(startElement, endElement);
+ movePageCommand = new MovePageDownCommand(
+ startElement,
+ endElement,
+ this.pagesContainer,
+ this.pagesContainerWrapper,
+ scrollTo
+ );
}
- if (scrollTo) {
- const { width } = startElement.getBoundingClientRect();
- const vector = endIndex !== -1 && startIndex > endIndex ? 0 - width : width;
-
- this.pagesContainerWrapper.scroll({
- left: this.pagesContainerWrapper.scrollLeft + vector,
- });
- }
+ movePageCommand.execute();
+ return movePageCommand;
}
addFiles(nextSiblingElement, blank = false) {
@@ -165,56 +197,22 @@ class PdfContainer {
async addFilesBlank(nextSiblingElement) {
- const pdfContent = `
- %PDF-1.4
- 1 0 obj
- << /Type /Catalog /Pages 2 0 R >>
- endobj
- 2 0 obj
- << /Type /Pages /Kids [3 0 R] /Count 1 >>
- endobj
- 3 0 obj
- << /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Contents 5 0 R >>
- endobj
- 5 0 obj
- << /Length 44 >>
- stream
- 0 0 0 595 0 842 re
- W
- n
- endstream
- endobj
- xref
- 0 6
- 0000000000 65535 f
- 0000000010 00000 n
- 0000000071 00000 n
- 0000000121 00000 n
- 0000000205 00000 n
- 0000000400 00000 n
- trailer
- << /Size 6 /Root 1 0 R >>
- startxref
- 278
- %%EOF
- `;
- const blob = new Blob([pdfContent], { type: 'application/pdf' });
- const url = URL.createObjectURL(blob);
- const file = new File([blob], "blank_page.pdf", { type: "application/pdf" });
- await this.addPdfFile(file, nextSiblingElement);
+ let doc = await PDFLib.PDFDocument.create();
+ let docBytes = await doc.save();
+
+ const url = URL.createObjectURL(new Blob([docBytes], { type: 'application/pdf' }));
+
+ const renderer = await this.toRenderer(url);
+
+ await this.addPdfFile(renderer, doc, nextSiblingElement);
}
rotateElement(element, deg) {
- var lastTransform = element.style.rotate;
- if (!lastTransform) {
- lastTransform = "0";
- }
- const lastAngle = parseInt(lastTransform.replace(/[^\d-]/g, ""));
- const newAngle = lastAngle + deg;
-
- element.style.rotate = newAngle + "deg";
+ let rotateCommand = new RotateElementCommand(element, deg);
+ rotateCommand.execute();
+ return rotateCommand;
}
async addPdfFile(renderer, pdfDocument, nextSiblingElement) {
@@ -309,6 +307,7 @@ class PdfContainer {
}
rotateAll(deg) {
+ let elementsToRotate = [];
for (let i = 0; i < this.pagesContainer.childNodes.length; i++) {
const child = this.pagesContainer.children[i];
if (!child) continue;
@@ -320,8 +319,13 @@ class PdfContainer {
const img = child.querySelector("img");
if (!img) continue;
- this.rotateElement(img, deg);
+ elementsToRotate.push(img);
}
+
+ let rotateAllCommand = new RotateAllCommand(elementsToRotate, deg);
+ rotateAllCommand.execute();
+
+ this.undoManager.pushUndoClearRedo(rotateAllCommand);
}
removeAllElements(){
@@ -336,34 +340,13 @@ class PdfContainer {
deleteSelected() {
window.selectedPages.sort((a, b) => a - b);
- let deletions = 0;
+ let removeSelectedCommand = new RemoveSelectedCommand(
+ this.pagesContainer,
+ window.selectedPages,
+ this.updatePageNumbersAndCheckboxes
+ );
- window.selectedPages.forEach((pageIndex) => {
- const adjustedIndex = pageIndex - 1 - deletions;
- const child = this.pagesContainer.children[adjustedIndex];
- if (child) {
- this.pagesContainer.removeChild(child);
- deletions++;
- }
- });
-
- if (this.pagesContainer.childElementCount === 0) {
- const filenameInput = document.getElementById("filename-input");
- const filenameParagraph = document.getElementById("filename");
- const downloadBtn = document.getElementById("export-button");
-
- if (filenameInput)
- filenameInput.disabled = true;
- filenameInput.value = "";
- if (filenameParagraph)
- filenameParagraph.innerText = "";
-
- downloadBtn.disabled = true;
- }
-
- window.selectedPages = [];
- this.updatePageNumbersAndCheckboxes();
- document.dispatchEvent(new Event("selectedPagesUpdated"));
+ this.undoManager.pushUndoClearRedo(removeSelectedCommand);
}
toggleSelectAll() {
@@ -531,34 +514,17 @@ class PdfContainer {
splitAll() {
const allPages = this.pagesContainer.querySelectorAll(".page-container");
+ let splitAllCommand = new SplitAllCommand(
+ allPages,
+ window.selectPage,
+ window.selectedPages,
+ "split-before"
+ );
+ splitAllCommand.execute();
- if (!window.selectPage) {
- const hasSplit = this.pagesContainer.querySelectorAll(".split-before").length > 0;
- if (hasSplit) {
- allPages.forEach(page => {
- page.classList.remove("split-before");
- });
- } else {
- allPages.forEach(page => {
- page.classList.add("split-before");
- });
- }
- return;
- }
-
- allPages.forEach((page, index) => {
- const pageIndex = index;
- if (window.selectPage && !window.selectedPages.includes(pageIndex)) return;
-
- if (page.classList.contains("split-before")) {
- page.classList.remove("split-before");
- } else {
- page.classList.add("split-before");
- }
- });
+ this.undoManager.pushUndoClearRedo(splitAllCommand);
}
-
async splitPDF(baseDocBytes, splitters) {
const baseDocument = await PDFLib.PDFDocument.load(baseDocBytes);
const pageNum = baseDocument.getPages().length;
diff --git a/src/main/resources/static/js/multitool/UndoManager.js b/src/main/resources/static/js/multitool/UndoManager.js
new file mode 100644
index 00000000..39202f3f
--- /dev/null
+++ b/src/main/resources/static/js/multitool/UndoManager.js
@@ -0,0 +1,65 @@
+export class UndoManager {
+ _undoStack;
+ _redoStack;
+
+ constructor() {
+ this._undoStack = [];
+ this._redoStack = [];
+ }
+
+ pushUndo(command) {
+ this._undoStack.push(command);
+ this._dispatchStateChange();
+ }
+
+ pushRedo(command) {
+ this._redoStack.push(command);
+ this._dispatchStateChange();
+ }
+
+ pushUndoClearRedo(command) {
+ this._undoStack.push(command);
+ this._redoStack = [];
+ this._dispatchStateChange();
+ }
+
+ undo() {
+ if (!this.canUndo()) return;
+
+ let cmd = this._undoStack.pop();
+ cmd.undo();
+
+ this._redoStack.push(cmd);
+ this._dispatchStateChange();
+ }
+
+ canUndo() {
+ return this._undoStack && this._undoStack.length > 0;
+ }
+
+ redo() {
+ if (!this.canRedo()) return;
+
+ let cmd = this._redoStack.pop();
+ cmd.redo();
+
+ this._undoStack.push(cmd);
+ this._dispatchStateChange();
+ }
+
+ canRedo() {
+ return this._redoStack && this._redoStack.length > 0;
+ }
+
+ _dispatchStateChange() {
+ document.dispatchEvent(
+ new CustomEvent("undo-manager-update", {
+ bubbles: true,
+ detail: {
+ canUndo: this.canUndo(),
+ canRedo: this.canRedo(),
+ },
+ })
+ );
+ }
+}
diff --git a/src/main/resources/static/js/multitool/commands/command.js b/src/main/resources/static/js/multitool/commands/command.js
new file mode 100644
index 00000000..a4ae5fdf
--- /dev/null
+++ b/src/main/resources/static/js/multitool/commands/command.js
@@ -0,0 +1,5 @@
+export class Command {
+ execute() {}
+ undo() {}
+ redo() {}
+}
diff --git a/src/main/resources/static/js/multitool/commands/delete-page.js b/src/main/resources/static/js/multitool/commands/delete-page.js
new file mode 100644
index 00000000..f9a00e7a
--- /dev/null
+++ b/src/main/resources/static/js/multitool/commands/delete-page.js
@@ -0,0 +1,74 @@
+import { Command } from "./command.js";
+
+export class DeletePageCommand extends Command {
+ constructor(element, pagesContainer) {
+ super();
+
+ this.element = element;
+ this.pagesContainer = pagesContainer;
+
+ this.filenameInputValue = document.getElementById("filename-input").value;
+
+ const filenameParagraph = document.getElementById("filename");
+ this.filenameParagraphText = filenameParagraph ? filenameParagraph.innerText : "";
+ }
+
+ execute() {
+ this.nextSibling = this.element.nextSibling;
+
+ this.pagesContainer.removeChild(this.element);
+ if (this.pagesContainer.childElementCount === 0) {
+ const filenameInput = document.getElementById("filename-input");
+ const filenameParagraph = document.getElementById("filename");
+ const downloadBtn = document.getElementById("export-button");
+
+ filenameInput.disabled = true;
+ filenameInput.value = "";
+ filenameParagraph.innerText = "";
+
+ downloadBtn.disabled = true;
+ }
+ }
+
+ undo() {
+ let node = this.nextSibling;
+ if (node) this.pagesContainer.insertBefore(this.element, node);
+ else this.pagesContainer.appendChild(this.element);
+
+ const pageNumberElement = this.element.querySelector(".page-number");
+ if (pageNumberElement) {
+ this.element.removeChild(pageNumberElement);
+ }
+
+ const filenameInput = document.getElementById("filename-input");
+ const filenameParagraph = document.getElementById("filename");
+ const downloadBtn = document.getElementById("export-button");
+
+ filenameInput.disabled = false;
+ filenameInput.value = this.filenameInputValue;
+ if (this.filenameParagraph)
+ filenameParagraph.innerText = this.filenameParagraphText;
+
+ downloadBtn.disabled = false;
+ }
+
+ redo() {
+ const pageNumberElement = this.element.querySelector(".page-number");
+ if (pageNumberElement) {
+ this.element.removeChild(pageNumberElement);
+ }
+
+ this.pagesContainer.removeChild(this.element);
+ if (this.pagesContainer.childElementCount === 0) {
+ const filenameInput = document.getElementById("filename-input");
+ const filenameParagraph = document.getElementById("filename");
+ const downloadBtn = document.getElementById("export-button");
+
+ filenameInput.disabled = true;
+ filenameInput.value = "";
+ filenameParagraph.innerText = "";
+
+ downloadBtn.disabled = true;
+ }
+ }
+}
diff --git a/src/main/resources/static/js/multitool/commands/move-page.js b/src/main/resources/static/js/multitool/commands/move-page.js
new file mode 100644
index 00000000..6f7b185e
--- /dev/null
+++ b/src/main/resources/static/js/multitool/commands/move-page.js
@@ -0,0 +1,133 @@
+import { Command } from "./command.js";
+
+export class AbstractMovePageCommand extends Command {
+ constructor(
+ startElement,
+ endElement,
+ pagesContainer,
+ pagesContainerWrapper,
+ scrollTo = false
+ ) {
+ super();
+
+ this.pagesContainer = pagesContainer;
+ const childArray = Array.from(this.pagesContainer.childNodes);
+
+ this.startIndex = childArray.indexOf(startElement);
+ this.endIndex = childArray.indexOf(endElement);
+
+ this.startElement = startElement;
+ this.endElement = endElement;
+
+ this.scrollTo = scrollTo;
+ this.pagesContainerWrapper = pagesContainerWrapper;
+ }
+
+ execute() {
+ // Check & remove page number elements here too if they exist because Firefox doesn't fire the relevant event on page move.
+ const pageNumberElement = this.startElement.querySelector(".page-number");
+ if (pageNumberElement) {
+ this.startElement.removeChild(pageNumberElement);
+ }
+
+ this.pagesContainer.removeChild(this.startElement);
+ if (!this.endElement) {
+ this.pagesContainer.append(this.startElement);
+ } else {
+ this.pagesContainer.insertBefore(this.startElement, this.endElement);
+ }
+
+ if (this.scrollTo) {
+ const { width } = this.startElement.getBoundingClientRect();
+ const vector =
+ this.endIndex !== -1 && this.startIndex > this.endIndex
+ ? 0 - width
+ : width;
+
+ this.pagesContainerWrapper.scroll({
+ left: this.pagesContainerWrapper.scrollLeft + vector,
+ });
+ }
+ }
+
+ undo() {
+ // Requires overriding in child classes
+
+ }
+
+ redo() {
+ this.execute();
+ }
+}
+
+export class MovePageUpCommand extends AbstractMovePageCommand {
+ constructor(
+ startElement,
+ endElement,
+ pagesContainer,
+ pagesContainerWrapper,
+ scrollTo = false
+ ) {
+ super(startElement, endElement, pagesContainer, pagesContainerWrapper, scrollTo);
+ }
+
+ undo() {
+ if (this.endElement) {
+ this.pagesContainer.removeChild(this.endElement);
+ this.startElement.insertAdjacentElement("beforebegin", this.endElement);
+ }
+
+ if (this.scrollTo) {
+ const { width } = this.startElement.getBoundingClientRect();
+ const vector =
+ this.endIndex === -1 || this.startIndex <= this.endIndex
+ ? 0 - width
+ : width;
+
+ this.pagesContainerWrapper.scroll({
+ left: this.pagesContainerWrapper.scrollLeft - vector,
+ });
+ }
+ }
+
+ redo() {
+ this.execute();
+ }
+}
+
+export class MovePageDownCommand extends AbstractMovePageCommand {
+ constructor(
+ startElement,
+ endElement,
+ pagesContainer,
+ pagesContainerWrapper,
+ scrollTo = false
+ ) {
+ super(startElement, endElement, pagesContainer, pagesContainerWrapper, scrollTo);
+ }
+
+ undo() {
+ let previousElement = this.startElement.previousSibling;
+
+ if (this.startElement) {
+ this.pagesContainer.removeChild(this.startElement);
+ previousElement.insertAdjacentElement("beforebegin", this.startElement);
+ }
+
+ if (this.scrollTo) {
+ const { width } = this.startElement.getBoundingClientRect();
+ const vector =
+ this.endIndex === -1 || this.startIndex <= this.endIndex
+ ? 0 - width
+ : width;
+
+ this.pagesContainerWrapper.scroll({
+ left: this.pagesContainerWrapper.scrollLeft - vector,
+ });
+ }
+ }
+
+ redo() {
+ this.execute();
+ }
+}
diff --git a/src/main/resources/static/js/multitool/commands/remove.js b/src/main/resources/static/js/multitool/commands/remove.js
new file mode 100644
index 00000000..0c2dc14e
--- /dev/null
+++ b/src/main/resources/static/js/multitool/commands/remove.js
@@ -0,0 +1,101 @@
+import { Command } from "./command.js";
+
+export class RemoveSelectedCommand extends Command {
+ constructor(pagesContainer, selectedPages, updatePageNumbersAndCheckboxes) {
+ super();
+ this.pagesContainer = pagesContainer;
+ this.selectedPages = selectedPages;
+
+ this.deletedChildren = [];
+
+ if (updatePageNumbersAndCheckboxes) {
+ this.updatePageNumbersAndCheckboxes = updatePageNumbersAndCheckboxes;
+ } else {
+ const pageDivs = document.querySelectorAll(".pdf-actions_container");
+
+ pageDivs.forEach((div, index) => {
+ const pageNumber = index + 1;
+ const checkbox = div.querySelector(".pdf-actions_checkbox");
+ checkbox.id = `selectPageCheckbox-${pageNumber}`;
+ checkbox.setAttribute("data-page-number", pageNumber);
+ checkbox.checked = window.selectedPages.includes(pageNumber);
+ });
+ }
+
+ const filenameInput = document.getElementById("filename-input");
+ const filenameParagraph = document.getElementById("filename");
+
+ this.originalFilenameInputValue = filenameInput ? filenameInput.value : "";
+ if (filenameParagraph)
+ this.originalFilenameParagraphText = filenameParagraph.innerText;
+ }
+
+ execute() {
+ let deletions = 0;
+
+ this.selectedPages.forEach((pageIndex) => {
+ const adjustedIndex = pageIndex - 1 - deletions;
+ const child = this.pagesContainer.children[adjustedIndex];
+ if (child) {
+ this.pagesContainer.removeChild(child);
+ deletions++;
+
+ this.deletedChildren.push({
+ idx: adjustedIndex,
+ childNode: child,
+ });
+ }
+ });
+
+ if (this.pagesContainer.childElementCount === 0) {
+ const filenameInput = document.getElementById("filename-input");
+ const filenameParagraph = document.getElementById("filename");
+ const downloadBtn = document.getElementById("export-button");
+
+ if (filenameInput) filenameInput.disabled = true;
+ filenameInput.value = "";
+ if (filenameParagraph) filenameParagraph.innerText = "";
+
+ downloadBtn.disabled = true;
+ }
+
+ window.selectedPages = [];
+ this.updatePageNumbersAndCheckboxes();
+ document.dispatchEvent(new Event("selectedPagesUpdated"));
+ }
+
+ undo() {
+ while (this.deletedChildren.length > 0) {
+ let deletedChild = this.deletedChildren.pop();
+ if (this.pagesContainer.children.length <= deletedChild.idx)
+ this.pagesContainer.appendChild(deletedChild.childNode);
+ else {
+ this.pagesContainer.insertBefore(
+ deletedChild.childNode,
+ this.pagesContainer.children[deletedChild.idx]
+ );
+ }
+ }
+
+ if (this.pagesContainer.childElementCount > 0) {
+ const filenameInput = document.getElementById("filename-input");
+ const filenameParagraph = document.getElementById("filename");
+ const downloadBtn = document.getElementById("export-button");
+
+ if (filenameInput) filenameInput.disabled = false;
+ filenameInput.value = this.originalFilenameInputValue;
+ if (filenameParagraph)
+ filenameParagraph.innerText = this.originalFilenameParagraphText;
+
+ downloadBtn.disabled = false;
+ }
+
+ window.selectedPages = this.selectedPages;
+ this.updatePageNumbersAndCheckboxes();
+ document.dispatchEvent(new Event("selectedPagesUpdated"));
+ }
+
+ redo() {
+ this.execute();
+ }
+}
diff --git a/src/main/resources/static/js/multitool/commands/rotate.js b/src/main/resources/static/js/multitool/commands/rotate.js
new file mode 100644
index 00000000..6fb08cb9
--- /dev/null
+++ b/src/main/resources/static/js/multitool/commands/rotate.js
@@ -0,0 +1,74 @@
+import { Command } from "./command.js";
+
+export class RotateElementCommand extends Command {
+ constructor(element, degree) {
+ super();
+ this.element = element;
+ this.degree = degree;
+ }
+
+ execute() {
+ let lastTransform = this.element.style.rotate;
+ if (!lastTransform) {
+ lastTransform = "0";
+ }
+ const lastAngle = parseInt(lastTransform.replace(/[^\d-]/g, ""));
+ const newAngle = lastAngle + parseInt(this.degree);
+
+ this.element.style.rotate = newAngle + "deg";
+ }
+
+ undo() {
+ let lastTransform = this.element.style.rotate;
+ if (!lastTransform) {
+ lastTransform = "0";
+ }
+
+ const currentAngle = parseInt(lastTransform.replace(/[^\d-]/g, ""));
+ const undoAngle = currentAngle + -parseInt(this.degree);
+
+ this.element.style.rotate = undoAngle + "deg";
+ }
+
+ redo() {
+ this.execute();
+ }
+}
+
+export class RotateAllCommand extends Command {
+ constructor(elements, degree) {
+ super();
+ this.elements = elements;
+ this.degree = degree;
+ }
+
+ execute() {
+ for (let element of this.elements) {
+ let lastTransform = element.style.rotate;
+ if (!lastTransform) {
+ lastTransform = "0";
+ }
+ const lastAngle = parseInt(lastTransform.replace(/[^\d-]/g, ""));
+ const newAngle = lastAngle + this.degree;
+
+ element.style.rotate = newAngle + "deg";
+ }
+ }
+
+ undo() {
+ for (let element of this.elements) {
+ let lastTransform = element.style.rotate;
+ if (!lastTransform) {
+ lastTransform = "0";
+ }
+ const currentAngle = parseInt(lastTransform.replace(/[^\d-]/g, ""));
+ const undoAngle = currentAngle + -this.degree;
+
+ element.style.rotate = undoAngle + "deg";
+ }
+ }
+
+ redo() {
+ this.execute();
+ }
+}
diff --git a/src/main/resources/static/js/multitool/commands/select.js b/src/main/resources/static/js/multitool/commands/select.js
new file mode 100644
index 00000000..b76a25ca
--- /dev/null
+++ b/src/main/resources/static/js/multitool/commands/select.js
@@ -0,0 +1,59 @@
+import { Command } from "./command.js";
+
+export class SelectPageCommand extends Command {
+ constructor(pageNumber, checkbox) {
+ super();
+ this.pageNumber = pageNumber;
+ this.selectCheckbox = checkbox;
+ }
+
+ execute() {
+ if (this.selectCheckbox.checked) {
+ //adds to array of selected pages
+ window.selectedPages.push(this.pageNumber);
+ } else {
+ //remove page from selected pages array
+ const index = window.selectedPages.indexOf(this.pageNumber);
+ if (index !== -1) {
+ window.selectedPages.splice(index, 1);
+ }
+ }
+
+ if (window.selectedPages.length > 0 && !window.selectPage) {
+ window.toggleSelectPageVisibility();
+ }
+ if (window.selectedPages.length == 0 && window.selectPage) {
+ window.toggleSelectPageVisibility();
+ }
+
+ window.updateSelectedPagesDisplay();
+ }
+
+ undo() {
+ this.selectCheckbox.checked = !this.selectCheckbox.checked;
+ if (this.selectCheckbox.checked) {
+ //adds to array of selected pages
+ window.selectedPages.push(this.pageNumber);
+ } else {
+ //remove page from selected pages array
+ const index = window.selectedPages.indexOf(this.pageNumber);
+ if (index !== -1) {
+ window.selectedPages.splice(index, 1);
+ }
+ }
+
+ if (window.selectedPages.length > 0 && !window.selectPage) {
+ window.toggleSelectPageVisibility();
+ }
+ if (window.selectedPages.length == 0 && window.selectPage) {
+ window.toggleSelectPageVisibility();
+ }
+
+ window.updateSelectedPagesDisplay();
+ }
+
+ redo() {
+ this.selectCheckbox.checked = !this.selectCheckbox.checked;
+ this.execute();
+ }
+}
diff --git a/src/main/resources/static/js/multitool/commands/split.js b/src/main/resources/static/js/multitool/commands/split.js
new file mode 100644
index 00000000..3cbc32cd
--- /dev/null
+++ b/src/main/resources/static/js/multitool/commands/split.js
@@ -0,0 +1,101 @@
+import { Command } from "./command.js";
+
+export class SplitFileCommand extends Command {
+ constructor(element, splitClass) {
+ super();
+ this.element = element;
+ this.splitClass = splitClass;
+ }
+
+ execute() {
+ this.element.classList.toggle(this.splitClass);
+ }
+
+ undo() {
+ this.element.classList.toggle(this.splitClass);
+ }
+
+ redo() {
+ this.execute();
+ }
+}
+
+export class SplitAllCommand extends Command {
+ constructor(elements, isSelectedInWindow, selectedPages, splitClass) {
+ super();
+ this.elements = elements;
+ this.isSelectedInWindow = isSelectedInWindow;
+ this.selectedPages = selectedPages;
+ this.splitClass = splitClass;
+ }
+
+ execute() {
+ if (!this.isSelectedInWindow) {
+ const hasSplit = this._hasSplit(this.elements, this.splitClass);
+ if (hasSplit) {
+ this.elements.forEach((page) => {
+ page.classList.remove(this.splitClass);
+ });
+ } else {
+ this.elements.forEach((page) => {
+ page.classList.add(this.splitClass);
+ });
+ }
+ return;
+ }
+
+ this.elements.forEach((page, index) => {
+ const pageIndex = index;
+ if (this.isSelectedInWindow && !this.selectedPages.includes(pageIndex))
+ return;
+
+ if (page.classList.contains(this.splitClass)) {
+ page.classList.remove(this.splitClass);
+ } else {
+ page.classList.add(this.splitClass);
+ }
+ });
+ }
+
+ _hasSplit() {
+ if (!this.elements || this.elements.length == 0) return false;
+
+ for (const node of this.elements) {
+ if (node.classList.contains(this.splitClass)) return true;
+ }
+
+ return false;
+ }
+
+ undo() {
+ if (!this.isSelectedInWindow) {
+ const hasSplit = this._hasSplit(this.elements, this.splitClass);
+ if (hasSplit) {
+ this.elements.forEach((page) => {
+ page.classList.remove(this.splitClass);
+ });
+ } else {
+ this.elements.forEach((page) => {
+ page.classList.add(this.splitClass);
+ });
+ }
+ return;
+ }
+
+ this.elements.forEach((page, index) => {
+ const pageIndex = index;
+ if (this.isSelectedInWindow && !this.selectedPages.includes(pageIndex))
+ return;
+
+ if (page.classList.contains(this.splitClass)) {
+ page.classList.remove(this.splitClass);
+ } else {
+ page.classList.add(this.splitClass);
+ }
+ });
+ }
+
+ redo() {
+ this.execute();
+ }
+}
diff --git a/src/main/resources/templates/multi-tool.html b/src/main/resources/templates/multi-tool.html
index 2a47a1e9..14e5a85e 100644
--- a/src/main/resources/templates/multi-tool.html
+++ b/src/main/resources/templates/multi-tool.html
@@ -57,6 +57,27 @@
+
+
+
+
+