utf8 bug fix and scan pages (#113)
This commit is contained in:
@@ -31,10 +31,10 @@ navbar.convert=تحويل
|
||||
navbar.security=الأمان
|
||||
navbar.other=أخرى
|
||||
navbar.darkmode=الوضع الداكن
|
||||
navbar.pageOps = عمليات الصفحة
|
||||
navbar.pageOps=عمليات الصفحة
|
||||
|
||||
home.multiTool.title = أداة متعددة PDF
|
||||
home.multiTool.desc = دمج الصفحات وتدويرها وإعادة ترتيبها وإزالتها
|
||||
home.multiTool.title=أداة متعددة PDF
|
||||
home.multiTool.desc=دمج الصفحات وتدويرها وإعادة ترتيبها وإزالتها
|
||||
|
||||
home.merge.title=دمج ملفات
|
||||
home.merge.desc=دمج ملفات PDF متعددة في ملف واحد بسهولة.
|
||||
@@ -91,24 +91,40 @@ home.ocr.desc=\u064A\u0642\u0648\u0645 \u0628\u0631\u0646\u0627\u0645\u062C \u06
|
||||
home.extractImages.title=\u0627\u0633\u062A\u062E\u0631\u0627\u062C \u0627\u0644\u0635\u0648\u0631
|
||||
home.extractImages.desc=\u064A\u0633\u062A\u062E\u0631\u062C \u062C\u0645\u064A\u0639 \u0627\u0644\u0635\u0648\u0631 \u0645\u0646 \u0645\u0644\u0641 PDF \u0648\u064A\u062D\u0641\u0638\u0647\u0627 \u0641\u064A \u0627\u0644\u0631\u0645\u0632 \u0627\u0644\u0628\u0631\u064A\u062F\u064A
|
||||
|
||||
home.pdfToPDFA.title = \u062A\u062D\u0648\u064A\u0644 \u0645\u0644\u0641\u0627\u062A PDF \u0625\u0644\u0649 PDF / A
|
||||
home.pdfToPDFA.desc = \u062A\u062D\u0648\u064A\u0644 PDF \u0625\u0644\u0649 PDF / A \u0644\u0644\u062A\u062E\u0632\u064A\u0646 \u0637\u0648\u064A\u0644 \u0627\u0644\u0645\u062F\u0649
|
||||
home.pdfToPDFA.title=\u062A\u062D\u0648\u064A\u0644 \u0645\u0644\u0641\u0627\u062A PDF \u0625\u0644\u0649 PDF / A
|
||||
home.pdfToPDFA.desc=\u062A\u062D\u0648\u064A\u0644 PDF \u0625\u0644\u0649 PDF / A \u0644\u0644\u062A\u062E\u0632\u064A\u0646 \u0637\u0648\u064A\u0644 \u0627\u0644\u0645\u062F\u0649
|
||||
|
||||
|
||||
home.PDFToWord.title = تحويل PDF إلى Word
|
||||
home.PDFToWord.desc = تحويل PDF إلى تنسيقات Word (DOC و DOCX و ODT)
|
||||
home.PDFToWord.title=تحويل PDF إلى Word
|
||||
home.PDFToWord.desc=تحويل PDF إلى تنسيقات Word (DOC و DOCX و ODT)
|
||||
|
||||
home.PDFToPresentation.title = PDF للعرض التقديمي
|
||||
home.PDFToPresentation.desc = تحويل PDF إلى تنسيقات عرض تقديمي (PPT و PPTX و ODP)
|
||||
home.PDFToPresentation.title=PDF للعرض التقديمي
|
||||
home.PDFToPresentation.desc=تحويل PDF إلى تنسيقات عرض تقديمي (PPT و PPTX و ODP)
|
||||
|
||||
home.PDFToText.title = تحويل PDF إلى نص / RTF
|
||||
home.PDFToText.desc = تحويل PDF إلى تنسيق نص أو RTF
|
||||
home.PDFToText.title=تحويل PDF إلى نص / RTF
|
||||
home.PDFToText.desc=تحويل PDF إلى تنسيق نص أو RTF
|
||||
|
||||
home.PDFToHTML.title = تحويل PDF إلى HTML
|
||||
home.PDFToHTML.desc = تحويل PDF إلى تنسيق HTML
|
||||
home.PDFToHTML.title=تحويل PDF إلى HTML
|
||||
home.PDFToHTML.desc=تحويل PDF إلى تنسيق HTML
|
||||
|
||||
home.PDFToXML.title=تحويل PDF إلى XML
|
||||
home.PDFToXML.desc=تحويل PDF إلى تنسيق XML
|
||||
|
||||
|
||||
home.ScannerImageSplit.title=كشف / انقسام الصور الممسوحة ضوئيًا
|
||||
home.ScannerImageSplit.desc=تقسيم عدة صور من داخل صورة / ملف PDF
|
||||
|
||||
ScannerImageSplit.selectText.1=عتبة الزاوية:
|
||||
ScannerImageSplit.selectText.2=تعيين الحد الأدنى للزاوية المطلقة المطلوبة لتدوير الصورة (افتراضي: 10).
|
||||
ScannerImageSplit.selectText.3=التسامح:
|
||||
ScannerImageSplit.selectText.4=يحدد نطاق تباين اللون حول لون الخلفية المقدر (الافتراضي: 30).
|
||||
ScannerImageSplit.selectText.5=أدنى مساحة:
|
||||
ScannerImageSplit.selectText.6=تعيين الحد الأدنى لمنطقة الصورة (الافتراضي: 10000).
|
||||
ScannerImageSplit.selectText.7=الحد الأدنى لمنطقة المحيط:
|
||||
ScannerImageSplit.selectText.8=تعيين الحد الأدنى لمنطقة المحيط للصورة
|
||||
ScannerImageSplit.selectText.9=حجم الحدود:
|
||||
ScannerImageSplit.selectText.10=يضبط حجم الحدود المضافة والمزالة لمنع الحدود البيضاء في الإخراج (الافتراضي: 1).
|
||||
|
||||
home.PDFToXML.title = تحويل PDF إلى XML
|
||||
home.PDFToXML.desc = تحويل PDF إلى تنسيق XML
|
||||
|
||||
navbar.settings=\u0625\u0639\u062F\u0627\u062F\u0627\u062A
|
||||
settings.title=\u0627\u0644\u0625\u0639\u062F\u0627\u062F\u0627\u062A
|
||||
@@ -133,6 +149,8 @@ ocr.selectText.7=\u0641\u0631\u0636 \u0627\u0644\u062A\u0639\u0631\u0641 \u0627\
|
||||
ocr.selectText.8=\u0639\u0627\u062F\u064A (\u062E\u0637\u0623 \u0625\u0630\u0627 \u0643\u0627\u0646 PDF \u064A\u062D\u062A\u0648\u064A \u0639\u0644\u0649 \u0646\u0635)
|
||||
ocr.selectText.9=\u0625\u0639\u062F\u0627\u062F\u0627\u062A \u0625\u0636\u0627\u0641\u064A\u0629
|
||||
ocr.selectText.10=\u0648\u0636\u0639 \u0627\u0644\u062A\u0639\u0631\u0641 \u0627\u0644\u0636\u0648\u0626\u064A \u0639\u0644\u0649 \u0627\u0644\u062D\u0631\u0648\u0641
|
||||
ocr.selectText.11 = إزالة الصور بعد التعرف الضوئي على الحروف (يزيل كل الصور ، يكون مفيدًا فقط إذا كان جزءًا من خطوة التحويل)
|
||||
ocr.selectText.12 = نوع العرض (متقدم)
|
||||
ocr.help=\u064A\u0631\u062C\u0649 \u0642\u0631\u0627\u0621\u0629 \u0647\u0630\u0647 \u0627\u0644\u0648\u062B\u0627\u0626\u0642 \u062D\u0648\u0644 \u0643\u064A\u0641\u064A\u0629 \u0627\u0633\u062A\u062E\u062F\u0627\u0645 \u0647\u0630\u0627 \u0644\u0644\u063A\u0627\u062A \u0623\u062E\u0631\u0649 \u0648 / \u0623\u0648 \u0627\u0644\u0627\u0633\u062A\u062E\u062F\u0627\u0645 \u0644\u064A\u0633 \u0641\u064A \u0639\u0627\u0645\u0644 \u0627\u0644\u0625\u0631\u0633\u0627\u0621
|
||||
ocr.credit=\u062A\u0633\u062A\u062E\u062F\u0645 \u0647\u0630\u0647 \u0627\u0644\u062E\u062F\u0645\u0629 OCRmyPDF \u0648 Tesseract \u0644 OCR.
|
||||
ocr.submit=\u0645\u0639\u0627\u0644\u062C\u0629 PDF \u0628\u0627\u0633\u062A\u062E\u062F\u0627\u0645 OCR
|
||||
@@ -182,8 +200,8 @@ pdfOrganiser.header=منظم صفحات PDF
|
||||
pdfOrganiser.submit=إعادة ترتيب الصفحات
|
||||
|
||||
#multiTool
|
||||
multiTool.title = أداة متعددة PDF
|
||||
multiTool.header = أداة متعددة PDF
|
||||
multiTool.title=أداة متعددة PDF
|
||||
multiTool.header=أداة متعددة PDF
|
||||
|
||||
#pageRemover
|
||||
pageRemover.title=مزيل الصفحة
|
||||
@@ -329,32 +347,32 @@ pdfToPDFA.credit=\u062A\u0633\u062A\u062E\u062F\u0645 \u0647\u0630\u0647 \u0627\
|
||||
pdfToPDFA.submit=\u062A\u062D\u0648\u064A\u0644
|
||||
|
||||
|
||||
PDFToWord.title = تحويل PDF إلى Word
|
||||
PDFToWord.header = تحويل PDF إلى Word
|
||||
PDFToWord.selectText.1 = تنسيق ملف الإخراج
|
||||
PDFToWord.credit = تستخدم هذه الخدمة LibreOffice لتحويل الملفات.
|
||||
PDFToWord.submit = تحويل
|
||||
PDFToWord.title=تحويل PDF إلى Word
|
||||
PDFToWord.header=تحويل PDF إلى Word
|
||||
PDFToWord.selectText.1=تنسيق ملف الإخراج
|
||||
PDFToWord.credit=تستخدم هذه الخدمة LibreOffice لتحويل الملفات.
|
||||
PDFToWord.submit=تحويل
|
||||
|
||||
PDFToPresentation.title = PDF للعرض التقديمي
|
||||
PDFToPresentation.header = PDF للعرض التقديمي
|
||||
PDFToPresentation.selectText.1 = تنسيق ملف الإخراج
|
||||
PDFToPresentation.credit = تستخدم هذه الخدمة LibreOffice لتحويل الملف.
|
||||
PDFToPresentation.submit = تحويل
|
||||
PDFToPresentation.title=PDF للعرض التقديمي
|
||||
PDFToPresentation.header=PDF للعرض التقديمي
|
||||
PDFToPresentation.selectText.1=تنسيق ملف الإخراج
|
||||
PDFToPresentation.credit=تستخدم هذه الخدمة LibreOffice لتحويل الملف.
|
||||
PDFToPresentation.submit=تحويل
|
||||
|
||||
|
||||
PDFToText.title = تحويل PDF إلى نص / RTF
|
||||
PDFToText.header = تحويل PDF إلى نص / RTF
|
||||
PDFToText.selectText.1 = تنسيق ملف الإخراج
|
||||
PDFToText.credit = تستخدم هذه الخدمة LibreOffice لتحويل الملفات.
|
||||
PDFToText.submit = تحويل
|
||||
PDFToText.title=تحويل PDF إلى نص / RTF
|
||||
PDFToText.header=تحويل PDF إلى نص / RTF
|
||||
PDFToText.selectText.1=تنسيق ملف الإخراج
|
||||
PDFToText.credit=تستخدم هذه الخدمة LibreOffice لتحويل الملفات.
|
||||
PDFToText.submit=تحويل
|
||||
|
||||
|
||||
PDFToHTML.title = PDF إلى HTML
|
||||
PDFToHTML.header = PDF إلى HTML
|
||||
PDFToHTML.credit = تستخدم هذه الخدمة LibreOffice لتحويل الملفات.
|
||||
PDFToHTML.submit = تحويل
|
||||
PDFToHTML.title=PDF إلى HTML
|
||||
PDFToHTML.header=PDF إلى HTML
|
||||
PDFToHTML.credit=تستخدم هذه الخدمة LibreOffice لتحويل الملفات.
|
||||
PDFToHTML.submit=تحويل
|
||||
|
||||
PDFToXML.title = تحويل PDF إلى XML
|
||||
PDFToXML.header = تحويل PDF إلى XML
|
||||
PDFToXML.credit = تستخدم هذه الخدمة LibreOffice لتحويل الملفات.
|
||||
PDFToXML.submit = تحويل
|
||||
PDFToXML.title=تحويل PDF إلى XML
|
||||
PDFToXML.header=تحويل PDF إلى XML
|
||||
PDFToXML.credit=تستخدم هذه الخدمة LibreOffice لتحويل الملفات.
|
||||
PDFToXML.submit=تحويل
|
||||
|
||||
@@ -104,6 +104,20 @@ home.PDFToHTML.desc=PDF in HTML-Format konvertieren
|
||||
home.PDFToXML.title=PDF in XML
|
||||
home.PDFToXML.desc=PDF in XML-Format konvertieren
|
||||
|
||||
home.ScannerImageSplit.title=Gescannte Fotos erkennen/aufteilen
|
||||
home.ScannerImageSplit.desc=Teilt mehrere Fotos innerhalb eines Fotos/PDF
|
||||
|
||||
ScannerImageSplit.selectText.1=Winkelschwelle:
|
||||
ScannerImageSplit.selectText.2=Legt den minimalen absoluten Winkel fest, der erforderlich ist, damit das Bild gedreht werden kann (Standard: 10).
|
||||
ScannerImageSplit.selectText.3=Toleranz:
|
||||
ScannerImageSplit.selectText.4=Bestimmt den Bereich der Farbvariation um die geschätzte Hintergrundfarbe herum (Standard: 30).
|
||||
ScannerImageSplit.selectText.5=Mindestbereich:
|
||||
ScannerImageSplit.selectText.6=Legt den minimalen Bereichsschwellenwert für ein Foto fest (Standard: 10000).
|
||||
ScannerImageSplit.selectText.7=Minimaler Konturbereich:
|
||||
ScannerImageSplit.selectText.8=Legt den minimalen Konturbereichsschwellenwert für ein Foto fest
|
||||
ScannerImageSplit.selectText.9=Randgröße:
|
||||
ScannerImageSplit.selectText.10=Legt die Größe des hinzugefügten und entfernten Randes fest, um weiße Ränder in der Ausgabe zu verhindern (Standard: 1).
|
||||
|
||||
|
||||
navbar.settings=Einstellungen
|
||||
settings.title=Einstellungen
|
||||
@@ -128,6 +142,8 @@ ocr.selectText.7=OCR erzwingen, OCR wird jede Seite entfernen und alle ursprüng
|
||||
ocr.selectText.8=Normal (Fehler, wenn PDF Text enthält)
|
||||
ocr.selectText.9=Zusätzliche Einstellungen
|
||||
ocr.selectText.10=OCR-Modus
|
||||
ocr.selectText.11=Bilder nach OCR entfernen (Entfernt ALLE Bilder, nur sinnvoll, wenn Teil des Konvertierungsschritts)
|
||||
ocr.selectText.12=Rendertyp (Erweitert)
|
||||
ocr.help=Bitte lesen Sie diese Dokumentation, um zu erfahren, wie Sie dies für andere Sprachen verwenden und/oder nicht in Docker verwenden können
|
||||
ocr.credit=Dieser Dienst verwendet OCRmyPDF und Tesseract für OCR.
|
||||
ocr.submit=PDF mit OCR verarbeiten
|
||||
|
||||
@@ -105,8 +105,22 @@ home.PDFToHTML.desc=Convert PDF to HTML format
|
||||
home.PDFToXML.title=PDF to XML
|
||||
home.PDFToXML.desc=Convert PDF to XML format
|
||||
|
||||
home.ScannerImageSplit.title=Detect/Split Scanned photos
|
||||
home.ScannerImageSplit.desc=Splits multiple photos from within a photo/PDF
|
||||
|
||||
|
||||
|
||||
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).
|
||||
|
||||
navbar.settings=Settings
|
||||
settings.title=Settings
|
||||
settings.update=Update available
|
||||
@@ -118,6 +132,7 @@ settings.downloadOption.3=Download file
|
||||
settings.zipThreshold=Zip files when the number of downloaded files exceeds
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#OCR
|
||||
@@ -133,6 +148,8 @@ ocr.selectText.7=Force OCR, will OCR Every page removing all original text eleme
|
||||
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 OCRmyPDF and Tesseract for OCR.
|
||||
ocr.submit=Process PDF with OCR
|
||||
|
||||
@@ -104,6 +104,19 @@ home.PDFToHTML.desc=Convertir PDF a formato HTML
|
||||
home.PDFToXML.title=PDF a XML
|
||||
home.PDFToXML.desc=Convertir PDF a formato XML
|
||||
|
||||
home.ScannerImageSplit.title=Detectar/Dividir fotos escaneadas
|
||||
home.ScannerImageSplit.desc=Dividir varias fotos dentro de una foto/PDF
|
||||
|
||||
ScannerImageSplit.selectText.1=Umbral de ángulo:
|
||||
ScannerImageSplit.selectText.2=Establece el ángulo absoluto mínimo requerido para rotar la imagen (predeterminado: 10).
|
||||
ScannerImageSplit.selectText.3=Tolerancia:
|
||||
ScannerImageSplit.selectText.4=Determina el rango de variación de color alrededor del color de fondo estimado (predeterminado: 30).
|
||||
ScannerImageSplit.selectText.5=Área mínima:
|
||||
ScannerImageSplit.selectText.6=Establece el umbral mínimo de área para una foto (predeterminado: 10000).
|
||||
ScannerImageSplit.selectText.7=Área de contorno mínima:
|
||||
ScannerImageSplit.selectText.8=Establece el umbral mínimo del área de contorno para una foto
|
||||
ScannerImageSplit.selectText.9=Tamaño del borde:
|
||||
ScannerImageSplit.selectText.10=Establece el tamaño del borde agregado y eliminado para evitar bordes blancos en la salida (predeterminado: 1).
|
||||
|
||||
navbar.settings=Ajustes
|
||||
settings.title=Ajustes
|
||||
@@ -131,6 +144,8 @@ ocr.selectText.7=Fuerza OCR, OCR eliminará en cada página todo el texto origin
|
||||
ocr.selectText.8=Normal (Se producirá un error si el PDF contiene texto)
|
||||
ocr.selectText.9=Ajustes Adicionales
|
||||
ocr.selectText.10=Modo OCR
|
||||
ocr.selectText.11=Eliminar imágenes después de OCR (Elimina TODAS las imágenes, solo es útil si es parte del paso de conversión)
|
||||
ocr.selectText.12=Tipo de procesamiento (avanzado)
|
||||
ocr.help=Lea esta documentación sobre cómo usar esto para otros idiomas y/o no usarlo en docker
|
||||
ocr.credit=Este servicio utiliza OCRmyPDF y Tesseract para OCR.
|
||||
ocr.submit=Procesa PDF con OCR
|
||||
|
||||
@@ -110,6 +110,20 @@ home.PDFToHTML.desc=Convertir le PDF au format HTML
|
||||
home.PDFToXML.title=PDF vers XML
|
||||
home.PDFToXML.desc=Convertir le PDF au format XML
|
||||
|
||||
home.ScannerImageSplit.title=Détecter/diviser les photos numérisées
|
||||
home.ScannerImageSplit.desc=Divise plusieurs photos à partir d'une photo/PDF
|
||||
|
||||
ScannerImageSplit.selectText.1=Seuil d'angle :
|
||||
ScannerImageSplit.selectText.2=Définit l'angle absolu minimum requis pour la rotation de l'image (par défaut : 10).
|
||||
ScannerImageSplit.selectText.3=Tolérance :
|
||||
ScannerImageSplit.selectText.4=Détermine la plage de variation de couleur autour de la couleur d'arrière-plan estimée (par défaut : 30).
|
||||
ScannerImageSplit.selectText.5=Zone minimale :
|
||||
ScannerImageSplit.selectText.6=Définit le seuil de zone minimum pour une photo (par défaut : 10000).
|
||||
ScannerImageSplit.selectText.7=Zone de contour minimale :
|
||||
ScannerImageSplit.selectText.8=Définit le seuil de zone de contour minimum pour une photo
|
||||
ScannerImageSplit.selectText.9=Taille de la bordure :
|
||||
ScannerImageSplit.selectText.10=Définit la taille de la bordure ajoutée et supprimée pour éviter les bordures blanches dans la sortie (par défaut : 1).
|
||||
|
||||
navbar.settings=Paramètres
|
||||
settings.title=Paramètres
|
||||
settings.update=Mise à jour disponible
|
||||
@@ -134,6 +148,8 @@ ocr.selectText.7=Forcer l'OCR, OCR chaque page supprimera tous les éléments de
|
||||
ocr.selectText.8=Normal (Erreur si le PDF contient du texte)
|
||||
ocr.selectText.9=Paramètres supplémentaires
|
||||
ocr.selectText.10=Mode ROC
|
||||
ocr.selectText.11=Supprimer les images après l'OCR (Supprime TOUTES les images, utile uniquement si elles font partie de l'étape de conversion)
|
||||
ocr.selectText.12=Type de rendu (avancé)
|
||||
ocr.help=Veuillez lire cette documentation pour savoir comment l'utiliser pour d'autres langues et/ou une utilisation non dans docker
|
||||
ocr.credit=Ce service utilise OCRmyPDF et Tesseract pour l'OCR.
|
||||
ocr.submit=Traiter PDF avec OCR
|
||||
|
||||
35
src/main/resources/static/css/rainbow-mode.css
Normal file
35
src/main/resources/static/css/rainbow-mode.css
Normal file
@@ -0,0 +1,35 @@
|
||||
/* Rainbow Mode Styles */
|
||||
body {
|
||||
background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%);
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.dark-card {
|
||||
background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%) !important;
|
||||
color: white !important;
|
||||
}
|
||||
.jumbotron {
|
||||
background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%);
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.list-group {
|
||||
background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%) !important;
|
||||
color: fff !important;
|
||||
}
|
||||
.list-group-item {
|
||||
background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%) !important;
|
||||
color: fff !important;
|
||||
}
|
||||
#support-section {
|
||||
background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%) !important;
|
||||
}
|
||||
|
||||
|
||||
#pages-container-wrapper {
|
||||
--background-color: rgba(255, 255, 255, 0.046) !important;
|
||||
--scroll-bar-color: #4c4c4c !important;
|
||||
--scroll-bar-thumb: #d3d3d3 !important;
|
||||
--scroll-bar-thumb-hover: #ffffff !important;
|
||||
}
|
||||
|
||||
13
src/main/resources/static/images/scanner.svg
Normal file
13
src/main/resources/static/images/scanner.svg
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg version="1.1" id="Icons" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 32 32" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
|
||||
.st1{fill:none;stroke:#000000;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:10;}
|
||||
</style>
|
||||
<path class="st0" d="M30,20H3v6c0,1.1,0.9,2,2,2h23c1.1,0,2-0.9,2-2V20z"/>
|
||||
<line class="st0" x1="30" y1="20" x2="3" y2="4"/>
|
||||
<line class="st0" x1="7" y1="24" x2="10" y2="24"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 691 B |
286
src/main/resources/static/js/game.js
Normal file
286
src/main/resources/static/js/game.js
Normal file
@@ -0,0 +1,286 @@
|
||||
function initializeGame() {
|
||||
const gameContainer = document.getElementById('game-container');
|
||||
const player = document.getElementById('player');
|
||||
|
||||
let playerSize = gameContainer.clientWidth * 0.0625; // 5% of container width
|
||||
player.style.width = playerSize + 'px';
|
||||
player.style.height = playerSize + 'px';
|
||||
|
||||
let playerX = gameContainer.clientWidth / 2 - playerSize / 2;
|
||||
let playerY = gameContainer.clientHeight * 0.1;
|
||||
const scoreElement = document.getElementById('score');
|
||||
const levelElement = document.getElementById('level');
|
||||
const livesElement = document.getElementById('lives');
|
||||
const highScoreElement = document.getElementById('high-score');
|
||||
|
||||
let pdfSize = gameContainer.clientWidth * 0.0625; // 5% of container width
|
||||
let projectileWidth = gameContainer.clientWidth * 0.00625; // 0.5% of container width
|
||||
let projectileHeight = gameContainer.clientHeight * 0.01667; // 1% of container height
|
||||
|
||||
let paused = false;
|
||||
const fireRate = 200; // Time between shots in milliseconds
|
||||
let lastProjectileTime = 0;
|
||||
let lives = 3;
|
||||
let highScore = localStorage.getItem('highScore') ? parseInt(localStorage.getItem('highScore')) : 0;
|
||||
updateHighScore();
|
||||
|
||||
|
||||
|
||||
const keysPressed = {};
|
||||
const pdfs = [];
|
||||
const projectiles = [];
|
||||
let score = 0;
|
||||
let level = 1;
|
||||
let pdfSpeed = 1;
|
||||
let gameOver = false;
|
||||
|
||||
function handleKeys() {
|
||||
if (keysPressed['ArrowLeft']) {
|
||||
playerX -= 10;
|
||||
}
|
||||
if (keysPressed['ArrowRight']) {
|
||||
playerX += 10;
|
||||
}
|
||||
if (keysPressed[' '] && !gameOver) {
|
||||
const currentTime = new Date().getTime();
|
||||
if (currentTime - lastProjectileTime >= fireRate) {
|
||||
shootProjectile();
|
||||
lastProjectileTime = currentTime;
|
||||
}
|
||||
}
|
||||
updatePlayerPosition();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
document.addEventListener('keydown', (event) => {
|
||||
if (event.key === ' ') {
|
||||
event.preventDefault();
|
||||
}
|
||||
keysPressed[event.key] = true;
|
||||
handleKeys();
|
||||
});
|
||||
|
||||
document.addEventListener('keyup', (event) => {
|
||||
keysPressed[event.key] = false;
|
||||
});
|
||||
|
||||
|
||||
function updatePlayerPosition() {
|
||||
player.style.left = playerX + 'px';
|
||||
player.style.bottom = playerY + 'px';
|
||||
}
|
||||
|
||||
function updateLives() {
|
||||
livesElement.textContent = 'Lives: ' + lives;
|
||||
}
|
||||
|
||||
function updateHighScore() {
|
||||
highScoreElement.textContent = 'High Score: ' + highScore;
|
||||
}
|
||||
|
||||
|
||||
function shootProjectile() {
|
||||
const projectile = document.createElement('div');
|
||||
projectile.classList.add('projectile');
|
||||
projectile.style.backgroundColor = 'black';
|
||||
projectile.style.width = projectileWidth + 'px';
|
||||
projectile.style.height = projectileHeight + 'px';
|
||||
projectile.style.left = (playerX + playerSize / 2 - projectileWidth / 2) + 'px';
|
||||
projectile.style.top = (gameContainer.clientHeight - playerY - playerSize) + 'px';
|
||||
gameContainer.appendChild(projectile);
|
||||
projectiles.push(projectile);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function spawnPdf() {
|
||||
const pdf = document.createElement('img');
|
||||
pdf.src = 'images/file-earmark-pdf.svg';
|
||||
pdf.classList.add('pdf');
|
||||
pdf.style.width = pdfSize + 'px';
|
||||
pdf.style.height = pdfSize + 'px';
|
||||
pdf.style.left = Math.floor(Math.random() * (gameContainer.clientWidth - pdfSize)) + 'px';
|
||||
pdf.style.top = '0px';
|
||||
gameContainer.appendChild(pdf);
|
||||
pdfs.push(pdf);
|
||||
}
|
||||
|
||||
|
||||
function resetEnemies() {
|
||||
pdfs.forEach((pdf) => gameContainer.removeChild(pdf));
|
||||
pdfs.length = 0;
|
||||
}
|
||||
|
||||
|
||||
function updateGame() {
|
||||
if (gameOver || paused) return;
|
||||
|
||||
pdfs.forEach((pdf, pdfIndex) => {
|
||||
const pdfY = parseInt(pdf.style.top) + pdfSpeed;
|
||||
if (pdfY + 50 > gameContainer.clientHeight) {
|
||||
gameContainer.removeChild(pdf);
|
||||
pdfs.splice(pdfIndex, 1);
|
||||
|
||||
// Deduct 2 points when a PDF gets past the player
|
||||
score -= 0;
|
||||
updateScore();
|
||||
|
||||
// Decrease lives and check if game over
|
||||
lives--;
|
||||
updateLives();
|
||||
if (lives <= 0) {
|
||||
endGame();
|
||||
return;
|
||||
}
|
||||
|
||||
} else {
|
||||
pdf.style.top = pdfY + 'px';
|
||||
|
||||
// Check for collision with player
|
||||
if (collisionDetected(player, pdf)) {
|
||||
lives--;
|
||||
updateLives();
|
||||
resetEnemies();
|
||||
if (lives <= 0) {
|
||||
endGame();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
projectiles.forEach((projectile, projectileIndex) => {
|
||||
const projectileY = parseInt(projectile.style.top) - 10;
|
||||
if (projectileY < 0) {
|
||||
gameContainer.removeChild(projectile);
|
||||
projectiles.splice(projectileIndex, 1);
|
||||
} else {
|
||||
projectile.style.top = projectileY + 'px';
|
||||
}
|
||||
|
||||
for (let pdfIndex = 0; pdfIndex < pdfs.length; pdfIndex++) {
|
||||
const pdf = pdfs[pdfIndex];
|
||||
if (collisionDetected(projectile, pdf)) {
|
||||
gameContainer.removeChild(pdf);
|
||||
gameContainer.removeChild(projectile);
|
||||
pdfs.splice(pdfIndex, 1);
|
||||
projectiles.splice(projectileIndex, 1);
|
||||
score = score + 10;
|
||||
updateScore();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(updateGame, 1000 / 60);
|
||||
}
|
||||
function resetGame() {
|
||||
playerX = gameContainer.clientWidth / 2;
|
||||
playerY = 50;
|
||||
updatePlayerPosition();
|
||||
|
||||
pdfs.forEach((pdf) => gameContainer.removeChild(pdf));
|
||||
projectiles.forEach((projectile) => gameContainer.removeChild(projectile));
|
||||
|
||||
pdfs.length = 0;
|
||||
projectiles.length = 0;
|
||||
|
||||
score = 0;
|
||||
level = 1;
|
||||
lives = 3;
|
||||
|
||||
gameOver = false;
|
||||
|
||||
updateScore();
|
||||
updateLives();
|
||||
levelElement.textContent = 'Level: ' + level;
|
||||
pdfSpeed = 1;
|
||||
clearTimeout(spawnPdfTimeout); // Clear the existing spawnPdfTimeout
|
||||
setTimeout(updateGame, 1000 / 60);
|
||||
spawnPdfInterval();
|
||||
}
|
||||
|
||||
|
||||
|
||||
function updateScore() {
|
||||
scoreElement.textContent = 'Score: ' + score;
|
||||
checkLevelUp();
|
||||
}
|
||||
|
||||
|
||||
|
||||
function checkLevelUp() {
|
||||
const newLevel = Math.floor(score / 100) + 1;
|
||||
if (newLevel > level) {
|
||||
level = newLevel;
|
||||
levelElement.textContent = 'Level: ' + level;
|
||||
pdfSpeed += 1;
|
||||
}
|
||||
}
|
||||
|
||||
function collisionDetected(a, b) {
|
||||
const rectA = a.getBoundingClientRect();
|
||||
const rectB = b.getBoundingClientRect();
|
||||
return (
|
||||
rectA.left < rectB.right &&
|
||||
rectA.right > rectB.left &&
|
||||
rectA.top < rectB.bottom &&
|
||||
rectA.bottom > rectB.top
|
||||
);
|
||||
}
|
||||
|
||||
function endGame() {
|
||||
gameOver = true;
|
||||
if (score > highScore) {
|
||||
highScore = score;
|
||||
localStorage.setItem('highScore', highScore);
|
||||
updateHighScore();
|
||||
}
|
||||
alert('Game Over! Your final score is: ' + score);
|
||||
setTimeout(() => { // Wrap the resetGame() call in a setTimeout
|
||||
resetGame();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
let spawnPdfTimeout;
|
||||
|
||||
function spawnPdfInterval() {
|
||||
console.log("spawnPdfInterval");
|
||||
if (gameOver || paused) {
|
||||
console.log("spawnPdfInterval 2");
|
||||
clearTimeout(spawnPdfTimeout);
|
||||
return;
|
||||
}
|
||||
console.log("spawnPdfInterval 3");
|
||||
spawnPdf();
|
||||
spawnPdfTimeout = setTimeout(spawnPdfInterval, 1000 - level * 50);
|
||||
}
|
||||
|
||||
updatePlayerPosition();
|
||||
updateGame();
|
||||
spawnPdfInterval();
|
||||
|
||||
|
||||
document.addEventListener('visibilitychange', function() {
|
||||
if (document.hidden) {
|
||||
paused = true;
|
||||
} else {
|
||||
paused = false;
|
||||
updateGame();
|
||||
spawnPdfInterval();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
window.initializeGame = initializeGame;
|
||||
3
src/main/resources/static/rainbow.svg
Normal file
3
src/main/resources/static/rainbow.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-rainbow" viewBox="0 0 16 16">
|
||||
<path d="M8 4.5a7 7 0 0 0-7 7 .5.5 0 0 1-1 0 8 8 0 1 1 16 0 .5.5 0 0 1-1 0 7 7 0 0 0-7-7zm0 2a5 5 0 0 0-5 5 .5.5 0 0 1-1 0 6 6 0 1 1 12 0 .5.5 0 0 1-1 0 5 5 0 0 0-5-5zm0 2a3 3 0 0 0-3 3 .5.5 0 0 1-1 0 4 4 0 1 1 8 0 .5.5 0 0 1-1 0 3 3 0 0 0-3-3zm0 2a1 1 0 0 0-1 1 .5.5 0 0 1-1 0 2 2 0 1 1 4 0 .5.5 0 0 1-1 0 1 1 0 0 0-1-1z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 459 B |
22
src/main/resources/templates/about.html
Normal file
22
src/main/resources/templates/about.html
Normal file
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
|
||||
|
||||
<th:block th:insert="~{fragments/common :: head(title='<3')}"></th:block>
|
||||
|
||||
<body>
|
||||
<div id="page-container">
|
||||
<div id="content-wrap">
|
||||
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
||||
<br> <br>
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -28,193 +28,235 @@
|
||||
<!-- Custom -->
|
||||
<link rel="stylesheet" href="css/general.css">
|
||||
<link rel="stylesheet" th:href="@{css/dark-mode.css}" id="dark-mode-styles">
|
||||
|
||||
<link rel="stylesheet" th:href="@{css/rainbow-mode.css}" id="rainbow-mode-styles" disabled="true">
|
||||
<script>
|
||||
function toggleDarkMode() {
|
||||
var darkModeStyles = document.getElementById("dark-mode-styles");
|
||||
var darkModeIcon = document.getElementById("dark-mode-icon");
|
||||
var toggleCount = 0;
|
||||
var lastToggleTime = Date.now();
|
||||
|
||||
if (localStorage.getItem("dark-mode") == "on") {
|
||||
localStorage.setItem("dark-mode", "off");
|
||||
darkModeStyles.disabled = true;
|
||||
darkModeIcon.src = "sun.svg";
|
||||
} else {
|
||||
localStorage.setItem("dark-mode", "on");
|
||||
darkModeStyles.disabled = false;
|
||||
darkModeIcon.src = "moon.svg";
|
||||
}
|
||||
}
|
||||
function toggleDarkMode() {
|
||||
var currentTime = Date.now();
|
||||
if (currentTime - lastToggleTime < 1000) {
|
||||
toggleCount++;
|
||||
} else {
|
||||
toggleCount = 1;
|
||||
}
|
||||
lastToggleTime = currentTime;
|
||||
|
||||
var darkModeStyles = document.getElementById("dark-mode-styles");
|
||||
var rainbowModeStyles = document.getElementById("rainbow-mode-styles");
|
||||
var darkModeIcon = document.getElementById("dark-mode-icon");
|
||||
|
||||
if (toggleCount >= 18) {
|
||||
localStorage.setItem("dark-mode", "rainbow");
|
||||
darkModeStyles.disabled = true;
|
||||
rainbowModeStyles.disabled = false;
|
||||
darkModeIcon.src = "rainbow.svg";
|
||||
} else if (localStorage.getItem("dark-mode") == "on") {
|
||||
localStorage.setItem("dark-mode", "off");
|
||||
darkModeStyles.disabled = true;
|
||||
rainbowModeStyles.disabled = true;
|
||||
darkModeIcon.src = "sun.svg";
|
||||
} else {
|
||||
localStorage.setItem("dark-mode", "on");
|
||||
darkModeStyles.disabled = false;
|
||||
rainbowModeStyles.disabled = true;
|
||||
darkModeIcon.src = "moon.svg";
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
var darkModeStyles = document.getElementById("dark-mode-styles");
|
||||
var darkModeIcon = document.getElementById("dark-mode-icon");
|
||||
var darkModeStyles = document.getElementById("dark-mode-styles");
|
||||
var rainbowModeStyles = document.getElementById("rainbow-mode-styles");
|
||||
var darkModeIcon = document.getElementById("dark-mode-icon");
|
||||
|
||||
// Check if the user has already set a preference
|
||||
if (localStorage.getItem("dark-mode") == "on") {
|
||||
darkModeStyles.disabled = false;
|
||||
darkModeIcon.src = "moon.svg";
|
||||
} else if (localStorage.getItem("dark-mode") == "off") {
|
||||
darkModeStyles.disabled = true;
|
||||
darkModeIcon.src = "sun.svg";
|
||||
} else {
|
||||
// Check the OS's default dark mode setting
|
||||
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
|
||||
darkModeStyles.disabled = false;
|
||||
darkModeIcon.src = "moon.svg";
|
||||
} else {
|
||||
darkModeStyles.disabled = true;
|
||||
darkModeIcon.src = "sun.svg";
|
||||
}
|
||||
}
|
||||
if (localStorage.getItem("dark-mode") == "on") {
|
||||
darkModeStyles.disabled = false;
|
||||
rainbowModeStyles.disabled = true;
|
||||
darkModeIcon.src = "moon.svg";
|
||||
} else if (localStorage.getItem("dark-mode") == "off") {
|
||||
darkModeStyles.disabled = true;
|
||||
rainbowModeStyles.disabled = true;
|
||||
darkModeIcon.src = "sun.svg";
|
||||
} else if (localStorage.getItem("dark-mode") == "rainbow") {
|
||||
darkModeStyles.disabled = true;
|
||||
rainbowModeStyles.disabled = false;
|
||||
darkModeIcon.src = "rainbow.svg";
|
||||
} else {
|
||||
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
|
||||
darkModeStyles.disabled = false;
|
||||
rainbowModeStyles.disabled = true;
|
||||
darkModeIcon.src = "moon.svg";
|
||||
} else {
|
||||
darkModeStyles.disabled = true;
|
||||
rainbowModeStyles.disabled = true;
|
||||
darkModeIcon.src = "sun.svg";
|
||||
}
|
||||
}
|
||||
|
||||
// Attach the toggleDarkMode function to the click event of the dark mode toggle
|
||||
document.getElementById("dark-mode-toggle").addEventListener("click", function (event) {
|
||||
event.preventDefault();
|
||||
toggleDarkMode();
|
||||
});
|
||||
});
|
||||
document.getElementById("dark-mode-toggle").addEventListener("click", function (event) {
|
||||
event.preventDefault();
|
||||
toggleDarkMode();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<th:block th:fragment="fileSelector(name, multiple)" th:with="accept=${accept} ?: '*/*', inputText=${inputText} ?: #{pdfPrompt}">
|
||||
<div class="custom-file-chooser">
|
||||
<div class="custom-file">
|
||||
<input type="file" class="custom-file-input" th:name="${name}" th:id="${name}+'-input'" th:accept="${accept}" multiple>
|
||||
<label class="custom-file-label" th:for="${name}+'-input'" th:text="${inputText}"></label>
|
||||
</div>
|
||||
<div class="selected-files"></div>
|
||||
</div>
|
||||
<br>
|
||||
<div id="progressBarContainer" style="display: none; position: relative;">
|
||||
<div class="progress" style="height: 1rem;">
|
||||
<div id="progressBar" class="progress-bar progress-bar-striped progress-bar-animated bg-success" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%;">
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
<script>
|
||||
|
||||
|
||||
|
||||
$('form').submit(function(event) {
|
||||
var processing = "Processing..."
|
||||
var submitButtonText = $('#submitBtn').text()
|
||||
|
||||
$('#submitBtn').text('Processing...');
|
||||
console.log("start download code")
|
||||
var files = $('#fileInput-input')[0].files;
|
||||
var url = this.action;
|
||||
console.log(url)
|
||||
event.preventDefault(); // Prevent the default form handling behavior
|
||||
/* Check if ${multiple} is false */
|
||||
var multiple = [[${multiple}]] || false;
|
||||
var override = $('#override').val() || '';
|
||||
console.log("override=" + override)
|
||||
if (override === 'multi' || (!multiple && files.length > 1) && override !== 'single' ) {
|
||||
console.log("multi parallel download")
|
||||
submitMultiPdfForm(event,url);
|
||||
} else {
|
||||
console.log("start single download")
|
||||
$(document).ready(function() {
|
||||
|
||||
function loadGameScript(callback) {
|
||||
console.log('loadGameScript called');
|
||||
const script = document.createElement('script');
|
||||
script.src = 'js/game.js';
|
||||
script.onload = callback;
|
||||
document.body.appendChild(script);
|
||||
}
|
||||
let gameScriptLoaded = false;
|
||||
$('#show-game-btn').on('click', function() {
|
||||
console.log('Show game button clicked');
|
||||
if (!gameScriptLoaded) {
|
||||
console.log('Show game button load');
|
||||
loadGameScript(function() {
|
||||
console.log('Game script loaded');
|
||||
window.initializeGame();
|
||||
gameScriptLoaded = true;
|
||||
});
|
||||
}
|
||||
$('#game-container-wrapper').show();
|
||||
});
|
||||
|
||||
// Get the selected download option from localStorage
|
||||
const downloadOption = localStorage.getItem('downloadOption');
|
||||
|
||||
var formData = new FormData($('form')[0]);
|
||||
|
||||
// Send the request to the server using the fetch() API
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
}).then(response => {
|
||||
if (!response) {
|
||||
throw new Error('Received null response for file ' + i);
|
||||
}
|
||||
console.log("load single download")
|
||||
|
||||
|
||||
// Extract the filename from the Content-Disposition header, if present
|
||||
let filename = null;
|
||||
const contentDispositionHeader = response.headers.get('Content-Disposition');
|
||||
console.log(contentDispositionHeader)
|
||||
if (contentDispositionHeader && contentDispositionHeader.indexOf('attachment') !== -1) {
|
||||
filename = contentDispositionHeader.split('filename=')[1].replace(/"/g, '');
|
||||
} else {
|
||||
// If the Content-Disposition header is not present or does not contain the filename, use a default filename
|
||||
filename = 'download';
|
||||
}
|
||||
console.log("filename=" + filename)
|
||||
|
||||
$('form').submit(function(event) {
|
||||
|
||||
const contentType = response.headers.get('Content-Type');
|
||||
console.log("contentType=" + contentType)
|
||||
// Check if the response is a PDF or an image
|
||||
if (contentType.includes('pdf') || contentType.includes('image')) {
|
||||
response.blob().then(blob => {
|
||||
console.log("pdf/image")
|
||||
const boredWaiting = localStorage.getItem('boredWaiting');
|
||||
if (boredWaiting === 'enabled') {
|
||||
$('#show-game-btn').show();
|
||||
}
|
||||
var processing = "Processing..."
|
||||
var submitButtonText = $('#submitBtn').text()
|
||||
|
||||
$('#submitBtn').text('Processing...');
|
||||
console.log("start download code")
|
||||
var files = $('#fileInput-input')[0].files;
|
||||
var url = this.action;
|
||||
console.log(url)
|
||||
event.preventDefault(); // Prevent the default form handling behavior
|
||||
/* Check if ${multiple} is false */
|
||||
var multiple = [[${multiple}]] || false;
|
||||
var override = $('#override').val() || '';
|
||||
console.log("override=" + override)
|
||||
if (override === 'multi' || (!multiple && files.length > 1) && override !== 'single' ) {
|
||||
console.log("multi parallel download")
|
||||
submitMultiPdfForm(event,url);
|
||||
} else {
|
||||
console.log("start single download")
|
||||
|
||||
// Perform the appropriate action based on the download option
|
||||
if (downloadOption === 'sameWindow') {
|
||||
console.log("same window")
|
||||
// Get the selected download option from localStorage
|
||||
const downloadOption = localStorage.getItem('downloadOption');
|
||||
|
||||
// Open the file in the same window
|
||||
window.location.href = URL.createObjectURL(blob);
|
||||
} else if (downloadOption === 'newWindow') {
|
||||
console.log("new window")
|
||||
var formData = new FormData($('form')[0]);
|
||||
|
||||
// Send the request to the server using the fetch() API
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
}).then(response => {
|
||||
if (!response) {
|
||||
throw new Error('Received null response for file ' + i);
|
||||
}
|
||||
console.log("load single download")
|
||||
|
||||
// Open the file in a new window
|
||||
window.open(URL.createObjectURL(blob), '_blank');
|
||||
} else {
|
||||
console.log("else save")
|
||||
|
||||
// Extract the filename from the Content-Disposition header, if present
|
||||
let filename = null;
|
||||
const contentDispositionHeader = response.headers.get('Content-Disposition');
|
||||
console.log(contentDispositionHeader)
|
||||
if (contentDispositionHeader && contentDispositionHeader.indexOf('attachment') !== -1) {
|
||||
filename = decodeURIComponent(contentDispositionHeader.split('filename=')[1].replace(/"/g, ''));
|
||||
} else {
|
||||
// If the Content-Disposition header is not present or does not contain the filename, use a default filename
|
||||
filename = 'download';
|
||||
}
|
||||
console.log("filename=" + filename)
|
||||
|
||||
// Download the file
|
||||
|
||||
const contentType = response.headers.get('Content-Type');
|
||||
console.log("contentType=" + contentType)
|
||||
// Check if the response is a PDF or an image
|
||||
if (contentType.includes('pdf') || contentType.includes('image')) {
|
||||
response.blob().then(blob => {
|
||||
console.log("pdf/image")
|
||||
|
||||
// Perform the appropriate action based on the download option
|
||||
if (downloadOption === 'sameWindow') {
|
||||
console.log("same window")
|
||||
|
||||
// Open the file in the same window
|
||||
window.location.href = URL.createObjectURL(blob);
|
||||
} else if (downloadOption === 'newWindow') {
|
||||
console.log("new window")
|
||||
|
||||
// Open the file in a new window
|
||||
window.open(URL.createObjectURL(blob), '_blank');
|
||||
} else {
|
||||
console.log("else save")
|
||||
|
||||
// Download the file
|
||||
const link = document.createElement('a');
|
||||
link.href = URL.createObjectURL(blob);
|
||||
link.download = filename;
|
||||
link.click();
|
||||
}
|
||||
});
|
||||
} else if (contentType.includes('json')) {
|
||||
// Handle the JSON response
|
||||
response.json().then(data => {
|
||||
// Format the error message
|
||||
const errorMessage = JSON.stringify(data, null, 2);
|
||||
|
||||
// Display the error message in an alert
|
||||
alert(`An error occurred: ${errorMessage}`);
|
||||
});
|
||||
|
||||
} else {
|
||||
response.blob().then(blob => {
|
||||
console.log("else save 2 zip")
|
||||
|
||||
// For ZIP files or other file types, just download the file
|
||||
const link = document.createElement('a');
|
||||
link.href = URL.createObjectURL(blob);
|
||||
link.download = filename;
|
||||
link.click();
|
||||
}
|
||||
});
|
||||
} else if (contentType.includes('json')) {
|
||||
// Handle the JSON response
|
||||
response.json().then(data => {
|
||||
// Format the error message
|
||||
const errorMessage = JSON.stringify(data, null, 2);
|
||||
|
||||
// Display the error message in an alert
|
||||
alert(`An error occurred: ${errorMessage}`);
|
||||
});
|
||||
|
||||
} else {
|
||||
response.blob().then(blob => {
|
||||
console.log("else save 2 zip")
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.log("error listener")
|
||||
|
||||
// For ZIP files or other file types, just download the file
|
||||
const link = document.createElement('a');
|
||||
link.href = URL.createObjectURL(blob);
|
||||
link.download = filename;
|
||||
link.click();
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.log("error listener")
|
||||
|
||||
// Extract the error message and stack trace from the response
|
||||
const errorMessage = error.message;
|
||||
const stackTrace = error.stack;
|
||||
|
||||
// Create an error message to display to the user
|
||||
const message = `${errorMessage}\n\n${stackTrace}`;
|
||||
|
||||
$('#submitBtn').text(submitButtonText);
|
||||
// Extract the error message and stack trace from the response
|
||||
const errorMessage = error.message;
|
||||
const stackTrace = error.stack;
|
||||
|
||||
// Create an error message to display to the user
|
||||
const message = `${errorMessage}\n\n${stackTrace}`;
|
||||
|
||||
$('#submitBtn').text(submitButtonText);
|
||||
|
||||
// Display the error message to the user
|
||||
alert(message);
|
||||
|
||||
});
|
||||
|
||||
// Display the error message to the user
|
||||
alert(message);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
$('#submitBtn').text(submitButtonText);
|
||||
});
|
||||
|
||||
}
|
||||
$('#submitBtn').text(submitButtonText);
|
||||
});
|
||||
});
|
||||
async function submitMultiPdfForm(event, url) {
|
||||
// Get the selected PDF files
|
||||
let files = $('#fileInput-input')[0].files;
|
||||
@@ -272,7 +314,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
if (!contentDisposition) {
|
||||
//throw new Error('Content-Disposition header not found for file ' + i);
|
||||
} else {
|
||||
fileName = contentDisposition.split('filename=')[1].replace(/"/g, '');
|
||||
fileName = contentDisposition.split('filename=')[1].replace(/"/g, '');
|
||||
}
|
||||
console.log('Received response for file ' + i + ': ' + response);
|
||||
|
||||
@@ -346,10 +388,10 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
}
|
||||
}
|
||||
function updateProgressBar(progressBar, files) {
|
||||
let progress = ((progressBar.attr('aria-valuenow') / files.length) * 100) + (100 / files.length);
|
||||
progressBar.css('width', progress + '%');
|
||||
progressBar.attr('aria-valuenow', parseInt(progressBar.attr('aria-valuenow')) + 1);
|
||||
}
|
||||
let progress = ((progressBar.attr('aria-valuenow') / files.length) * 100) + (100 / files.length);
|
||||
progressBar.css('width', progress + '%');
|
||||
progressBar.attr('aria-valuenow', parseInt(progressBar.attr('aria-valuenow')) + 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -357,6 +399,92 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="custom-file-chooser">
|
||||
<div class="custom-file">
|
||||
<input type="file" class="custom-file-input" th:name="${name}" th:id="${name}+'-input'" th:accept="${accept}" multiple>
|
||||
<label class="custom-file-label" th:for="${name}+'-input'" th:text="${inputText}"></label>
|
||||
</div>
|
||||
<div class="selected-files"></div>
|
||||
</div>
|
||||
<br>
|
||||
<div id="progressBarContainer" style="display: none; position: relative;">
|
||||
<div class="progress" style="height: 1rem;">
|
||||
<div id="progressBar" class="progress-bar progress-bar-striped progress-bar-animated bg-success" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%;">
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary" id="show-game-btn" style="display:none;">Bored waiting?</button>
|
||||
|
||||
<div id="game-container-wrapper" class="game-container-wrapper" style="display: none;">
|
||||
<div id="game-container">
|
||||
<div id="lives">Lives: 3</div>
|
||||
<div id="score">Score: 0</div>
|
||||
<div id="high-score">High Score: 0</div>
|
||||
<div id="level">Level: 1</div>
|
||||
<img src="favicon.svg" class="player" id="player">
|
||||
</div>
|
||||
<style>
|
||||
#game-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 0;
|
||||
padding-bottom: 75%; /* 4:3 aspect ratio */
|
||||
background-color: transparent;
|
||||
margin: auto;
|
||||
overflow: hidden;
|
||||
border: 2px solid black; /* Add border */
|
||||
}
|
||||
|
||||
.pdf, .player, .projectile {
|
||||
position: absolute;
|
||||
}
|
||||
.pdf {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
.player {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
.projectile {
|
||||
background-color: black !important;
|
||||
width: 5px;
|
||||
height: 10px;
|
||||
}
|
||||
#score, #level, #lives, #high-score {
|
||||
color: black;
|
||||
font-family: sans-serif;
|
||||
position: absolute;
|
||||
font-size: calc(14px + 0.25vw); /* Reduced font size */
|
||||
}
|
||||
#score {
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
}
|
||||
#lives {
|
||||
top: 10px;
|
||||
left: calc(7vw); /* Adjusted position */
|
||||
}
|
||||
#high-score {
|
||||
top: 10px;
|
||||
left: calc(14vw); /* Adjusted position */
|
||||
}
|
||||
#level {
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
</style>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script th:inline="javascript">
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
|
||||
@@ -138,7 +138,7 @@ function compareVersions(version1, version2) {
|
||||
<ul class="navbar-nav mr-auto flex-nowrap">
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#" th:href="@{multi-tool}" th:classappend="${currentPage}=='multi-tool' ? 'active' : ''">
|
||||
<a class="nav-link" href="#" th:href="@{multi-tool}" th:classappend="${currentPage}=='multi-tool' ? 'active' : ''" th:title="#{home.multiTool.desc}">
|
||||
<img class="icon" src="images/tools.svg" alt="icon">
|
||||
<span class="icon-text" th:text="#{home.multiTool.title}"></span>
|
||||
</a>
|
||||
@@ -151,23 +151,23 @@ function compareVersions(version1, version2) {
|
||||
</a>
|
||||
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
|
||||
<!-- Existing menu items -->
|
||||
<a class="dropdown-item" href="#" th:href="@{merge-pdfs}" th:classappend="${currentPage}=='merge-pdfs' ? 'active' : ''">
|
||||
<a class="dropdown-item" href="#" th:href="@{merge-pdfs}" th:classappend="${currentPage}=='merge-pdfs' ? 'active' : ''" th:title="#{home.merge.desc}">
|
||||
<img class="icon" src="images/union.svg" alt="icon">
|
||||
<span class="icon-text" th:text="#{home.merge.title}"></span>
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" th:href="@{split-pdfs}" th:classappend="${currentPage}=='split-pdfs' ? 'active' : ''">
|
||||
<a class="dropdown-item" href="#" th:href="@{split-pdfs}" th:classappend="${currentPage}=='split-pdfs' ? 'active' : ''" th:title="#{home.split.desc}">
|
||||
<img class="icon" src="images/layout-split.svg" alt="icon">
|
||||
<span class="icon-text" th:text="#{home.split.title}"></span>
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" th:href="@{pdf-organizer}" th:classappend="${currentPage}=='pdf-organizer' ? 'active' : ''">
|
||||
<a class="dropdown-item" href="#" th:href="@{pdf-organizer}" th:classappend="${currentPage}=='pdf-organizer' ? 'active' : ''" th:title="#{home.pdfOrganiser.desc}">
|
||||
<img class="icon" src="images/sort-numeric-down.svg" alt="icon">
|
||||
<span class="icon-text" th:text="#{home.pdfOrganiser.title}"></span>
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" th:href="@{rotate-pdf}" th:classappend="${currentPage}=='rotate-pdf' ? 'active' : ''">
|
||||
<a class="dropdown-item" href="#" th:href="@{rotate-pdf}" th:classappend="${currentPage}=='rotate-pdf' ? 'active' : ''" th:title="#{home.rotate.desc}">
|
||||
<img class="icon" src="images/arrow-clockwise.svg" alt="icon">
|
||||
<span class="icon-text" th:text="#{home.rotate.title}"></span>
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" th:href="@{remove-pages}" th:classappend="${currentPage}=='remove-pages' ? 'active' : ''">
|
||||
<a class="dropdown-item" href="#" th:href="@{remove-pages}" th:classappend="${currentPage}=='remove-pages' ? 'active' : ''" th:title="#{home.removePages.desc}">
|
||||
<img class="icon" src="images/file-earmark-x.svg" alt="icon">
|
||||
<span class="icon-text" th:text="#{home.removePages.title}"></span>
|
||||
</a>
|
||||
@@ -181,51 +181,51 @@ function compareVersions(version1, version2) {
|
||||
</a>
|
||||
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
|
||||
<!-- Existing menu items -->
|
||||
<a class="dropdown-item" href="#" th:href="@{img-to-pdf}" th:classappend="${currentPage}=='img-to-pdf' ? 'active' : ''">
|
||||
<a class="dropdown-item" href="#" th:href="@{img-to-pdf}" th:classappend="${currentPage}=='img-to-pdf' ? 'active' : ''" th:title="#{home.imageToPdf.desc}">
|
||||
<img class="icon" src="images/image.svg" alt="icon" style="width: 16px; height: 16px; vertical-align: middle;">
|
||||
<span class="icon-text" th:text="#{home.imageToPdf.title}"></span>
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" th:href="@{file-to-pdf}" th:classappend="${currentPage}=='file-to-pdf' ? 'active' : ''">
|
||||
<a class="dropdown-item" href="#" th:href="@{file-to-pdf}" th:classappend="${currentPage}=='file-to-pdf' ? 'active' : ''" th:title="#{home.fileToPDF.desc}">
|
||||
<img class="icon" src="images/file.svg" alt="icon" style="width: 16px; height: 16px; vertical-align: middle;">
|
||||
<span class="icon-text" th:text="#{home.fileToPDF.title}"></span>
|
||||
</a>
|
||||
<hr class="dropdown-divider">
|
||||
<a class="dropdown-item" href="#" th:href="@{pdf-to-img}" th:classappend="${currentPage}=='pdf-to-img' ? 'active' : ''">
|
||||
<a class="dropdown-item" href="#" th:href="@{pdf-to-img}" th:classappend="${currentPage}=='pdf-to-img' ? 'active' : ''" th:title="#{home.pdfToImage.desc}">
|
||||
|
||||
|
||||
<img class="icon" src="images/image.svg" alt="icon">
|
||||
<span class="icon-text" th:text="#{home.pdfToImage.title}"></span>
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" th:href="@{pdf-to-word}" th:classappend="${currentPage}=='pdf-to-word' ? 'active' : ''">
|
||||
<a class="dropdown-item" href="#" th:href="@{pdf-to-word}" th:classappend="${currentPage}=='pdf-to-word' ? 'active' : ''" th:title="#{home.PDFToWord.desc}">
|
||||
|
||||
|
||||
<img class="icon" src="images/file-earmark-word.svg" alt="icon">
|
||||
<span class="icon-text" th:text="#{home.PDFToWord.title}"></span>
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" th:href="@{pdf-to-presentation}" th:classappend="${currentPage}=='pdf-to-presentation' ? 'active' : ''">
|
||||
<a class="dropdown-item" href="#" th:href="@{pdf-to-presentation}" th:classappend="${currentPage}=='pdf-to-presentation' ? 'active' : ''" th:title="#{home.PDFToPresentation.desc}">
|
||||
|
||||
|
||||
<img class="icon" src="images/file-earmark-ppt.svg" alt="icon">
|
||||
<span class="icon-text" th:text="#{home.PDFToPresentation.title}"></span>
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" th:href="@{pdf-to-text}" th:classappend="${currentPage}=='pdf-to-text' ? 'active' : ''">
|
||||
<a class="dropdown-item" href="#" th:href="@{pdf-to-text}" th:classappend="${currentPage}=='pdf-to-text' ? 'active' : ''" th:title="#{home.PDFToText.desc}">
|
||||
|
||||
|
||||
<img class="icon" src="images/filetype-txt.svg" alt="icon">
|
||||
<span class="icon-text" th:text="#{home.PDFToText.title}"></span>
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" th:href="@{pdf-to-html}" th:classappend="${currentPage}=='pdf-to-html' ? 'active' : ''">
|
||||
<a class="dropdown-item" href="#" th:href="@{pdf-to-html}" th:classappend="${currentPage}=='pdf-to-html' ? 'active' : ''" th:title="#{home.PDFToHTML.desc}">
|
||||
|
||||
|
||||
<img class="icon" src="images/filetype-html.svg" alt="icon">
|
||||
<span class="icon-text" th:text="#{home.PDFToHTML.title}"></span>
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" th:href="@{pdf-to-xml}" th:classappend="${currentPage}=='pdf-to-xml' ? 'active' : ''">
|
||||
<a class="dropdown-item" href="#" th:href="@{pdf-to-xml}" th:classappend="${currentPage}=='pdf-to-xml' ? 'active' : ''" th:title="#{home.PDFToXML.desc}">
|
||||
|
||||
<img class="icon" src="images/filetype-xml.svg" alt="icon">
|
||||
<span class="icon-text" th:text="#{home.PDFToXML.title}"></span>
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" th:href="@{pdf-to-pdfa}" th:classappend="${currentPage}=='pdf-to-pdfa' ? 'active' : ''">
|
||||
<a class="dropdown-item" href="#" th:href="@{pdf-to-pdfa}" th:classappend="${currentPage}=='pdf-to-pdfa' ? 'active' : ''" th:title="#{home.pdfToPDFA.desc}">
|
||||
|
||||
<img class="icon" src="images/file-earmark-pdf.svg" alt="icon">
|
||||
<span class="icon-text" th:text="#{home.pdfToPDFA.title}"></span>
|
||||
@@ -243,16 +243,16 @@ function compareVersions(version1, version2) {
|
||||
<img class="icon" src="images/shield-check.svg" alt="icon" style="width: 16px; height: 16px; vertical-align: middle;"> <span class="icon-text" th:text="#{navbar.security}"></span>
|
||||
</a>
|
||||
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
|
||||
<a class="dropdown-item" href="#" th:href="@{add-password}" th:classappend="${currentPage}=='add-password' ? 'active' : ''">
|
||||
<a class="dropdown-item" href="#" th:href="@{add-password}" th:classappend="${currentPage}=='add-password' ? 'active' : ''" th:title="#{home.addPassword.desc}">
|
||||
<img class="icon" src="images/lock.svg" alt="icon" style="width: 16px; height: 16px; vertical-align: middle;"> <span class="icon-text" th:text="#{home.addPassword.title}"></span>
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" th:href="@{remove-password}" th:classappend="${currentPage}=='remove-password' ? 'active' : ''">
|
||||
<a class="dropdown-item" href="#" th:href="@{remove-password}" th:classappend="${currentPage}=='remove-password' ? 'active' : ''" th:title="#{home.removePassword.desc}">
|
||||
<img class="icon" src="images/unlock.svg" alt="icon" style="width: 16px; height: 16px; vertical-align: middle;"> <span class="icon-text" th:text="#{home.removePassword.title}"></span>
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" th:href="@{change-permissions}" th:classappend="${currentPage}=='change-permissions' ? 'active' : ''">
|
||||
<a class="dropdown-item" href="#" th:href="@{change-permissions}" th:classappend="${currentPage}=='change-permissions' ? 'active' : ''" th:title="#{home.permissions.desc}">
|
||||
<img class="icon" src="images/shield-lock.svg" alt="icon" style="width: 16px; height: 16px; vertical-align: middle;"> <span class="icon-text" th:text="#{home.permissions.title}"></span>
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" th:href="@{add-watermark}" th:classappend="${currentPage}=='add-watermark' ? 'active' : ''">
|
||||
<a class="dropdown-item" href="#" th:href="@{add-watermark}" th:classappend="${currentPage}=='add-watermark' ? 'active' : ''" th:title="#{home.watermark.desc}">
|
||||
<img class="icon" src="images/droplet.svg" alt="icon" style="width: 16px; height: 16px; vertical-align: middle;"> <span class="icon-text" th:text="#{home.watermark.title}"></span>
|
||||
</a>
|
||||
</div>
|
||||
@@ -266,21 +266,24 @@ function compareVersions(version1, version2) {
|
||||
|
||||
</a>
|
||||
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
|
||||
<a class="dropdown-item" href="#" th:href="@{ocr-pdf}" th:classappend="${currentPage}=='ocr-pdf' ? 'active' : ''">
|
||||
<a class="dropdown-item" href="#" th:href="@{ocr-pdf}" th:classappend="${currentPage}=='ocr-pdf' ? 'active' : ''" th:title="#{home.ocr.desc}">
|
||||
<img class="icon" src="images/search.svg" alt="icon" style="width: 16px; height: 16px; vertical-align: middle;"> <span class="icon-text" th:text="#{home.ocr.title}"></span>
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" th:href="@{add-image}" th:classappend="${currentPage}=='add-image' ? 'active' : ''">
|
||||
<a class="dropdown-item" href="#" th:href="@{add-image}" th:classappend="${currentPage}=='add-image' ? 'active' : ''" th:title="#{home.addImage.desc}">
|
||||
<img class="icon" src="images/file-earmark-richtext.svg" alt="icon" style="width: 16px; height: 16px; vertical-align: middle;"> <span class="icon-text" th:text="#{home.addImage.title}"></span>
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" th:href="@{compress-pdf}" th:classappend="${currentPage}=='compress-pdf' ? 'active' : ''">
|
||||
<a class="dropdown-item" href="#" th:href="@{compress-pdf}" th:classappend="${currentPage}=='compress-pdf' ? 'active' : ''" th:title="#{home.compressPdfs.desc}">
|
||||
<img class="icon" src="images/file-zip.svg" alt="icon" style="width: 16px; height: 16px; vertical-align: middle;"> <span class="icon-text" th:text="#{home.compressPdfs.title}"></span>
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" th:href="@{extract-images}" th:classappend="${currentPage}=='extract-images' ? 'active' : ''">
|
||||
<a class="dropdown-item" href="#" th:href="@{extract-images}" th:classappend="${currentPage}=='extract-images' ? 'active' : ''" th:title="#{home.extractImages.desc}">
|
||||
<img class="icon" src="images/images.svg" alt="icon" style="width: 16px; height: 16px; vertical-align: middle;"> <span class="icon-text" th:text="#{home.extractImages.title}"></span>
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" th:href="@{change-metadata}" th:classappend="${currentPage}=='change-metadata' ? 'active' : ''">
|
||||
<a class="dropdown-item" href="#" th:href="@{change-metadata}" th:classappend="${currentPage}=='change-metadata' ? 'active' : ''" th:title="#{home.changeMetadata.desc}">
|
||||
<img class="icon" src="images/clipboard-data.svg" alt="icon" style="width: 16px; height: 16px; vertical-align: middle;"> <span class="icon-text" th:text="#{home.changeMetadata.title}"></span>
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" th:href="@{extract-image-scans}" th:classappend="${currentPage}=='extract-image-scans' ? 'active' : ''" th:title="#{home.ScannerImageSplit.desc}">
|
||||
<img class="icon" src="images/scanner.svg" alt="icon" style="width: 16px; height: 16px; vertical-align: middle;"> <span class="icon-text" th:text="#{home.ScannerImageSplit.title}"></span>
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
@@ -364,6 +367,12 @@ function compareVersions(version1, version2) {
|
||||
<input type="range" class="custom-range" min="1" max="9" step="1" id="zipThreshold" value="4">
|
||||
<span id="zipThresholdValue" class="ml-2"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input" id="boredWaiting">
|
||||
<label class="custom-control-label" for="boredWaiting">Bored Waiting? :)</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal" th:text="#{close}"></button>
|
||||
@@ -381,6 +390,7 @@ function compareVersions(version1, version2) {
|
||||
// Set the selected option in the dropdown
|
||||
document.getElementById('downloadOption').value = downloadOption;
|
||||
|
||||
|
||||
// Save the selected option to local storage when the dropdown value changes
|
||||
document.getElementById('downloadOption').addEventListener(
|
||||
'change',
|
||||
@@ -398,6 +408,8 @@ function compareVersions(version1, version2) {
|
||||
document.getElementById('zipThreshold').value = zipThreshold;
|
||||
document.getElementById('zipThresholdValue').textContent = zipThreshold;
|
||||
|
||||
|
||||
|
||||
// Save the selected value to local storage when the slider value changes
|
||||
document.getElementById('zipThreshold').addEventListener('input', function () {
|
||||
zipThreshold = this.value;
|
||||
@@ -405,6 +417,15 @@ function compareVersions(version1, version2) {
|
||||
localStorage.setItem('zipThreshold', zipThreshold);
|
||||
});
|
||||
|
||||
|
||||
var boredWaiting = localStorage.getItem('boredWaiting') || 'disabled';
|
||||
document.getElementById('boredWaiting').checked = boredWaiting === 'enabled';
|
||||
|
||||
document.getElementById('boredWaiting').addEventListener('change', function() {
|
||||
boredWaiting = this.checked ? 'enabled' : 'disabled';
|
||||
localStorage.setItem('boredWaiting', boredWaiting);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
@@ -75,42 +75,39 @@ filter: invert(0.2) sepia(2) saturate(50) hue-rotate(190deg);
|
||||
|
||||
<!-- Features -->
|
||||
<div class="features-container container">
|
||||
|
||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.multiTool.title}, cardText=#{home.multiTool.desc}, cardLink='multi-tool', svgPath='images/tools.svg')}"></div>
|
||||
|
||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.merge.title}, cardText=#{home.merge.desc}, cardLink='merge-pdfs', svgPath='images/union.svg')}"></div>
|
||||
|
||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.split.title}, cardText=#{home.split.desc}, cardLink='split-pdfs', svgPath='images/layout-split.svg')}"></div>
|
||||
|
||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.rotate.title}, cardText=#{home.rotate.desc}, cardLink='rotate-pdf', svgPath='images/arrow-clockwise.svg')}"></div>
|
||||
|
||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.imageToPdf.title}, cardText=#{home.imageToPdf.desc}, cardLink='img-to-pdf', svgPath='images/image.svg')}"></div>
|
||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.pdfToImage.title}, cardText=#{home.pdfToImage.desc}, cardLink='pdf-to-img', svgPath='images/image.svg')}"></div>
|
||||
|
||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.pdfOrganiser.title}, cardText=#{home.pdfOrganiser.desc}, cardLink='pdf-organizer', svgPath='images/sort-numeric-down.svg')}"></div>
|
||||
|
||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.addImage.title}, cardText=#{home.addImage.desc}, cardLink='add-image', svgPath='images/file-earmark-richtext.svg')}"></div>
|
||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.watermark.title}, cardText=#{home.watermark.desc}, cardLink='add-watermark', svgPath='images/droplet.svg')}"></div>
|
||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.fileToPDF.title}, cardText=#{home.fileToPDF.desc}, cardLink='file-to-pdf', svgPath='images/file.svg')}"></div>
|
||||
|
||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.fileToPDF.title}, cardText=#{home.fileToPDF.desc}, cardLink='file-to-pdf', svgPath='images/file.svg')}"></div>
|
||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.removePages.title}, cardText=#{home.removePages.desc}, cardLink='remove-pages', svgPath='images/file-earmark-x.svg')}"></div>
|
||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.addPassword.title}, cardText=#{home.addPassword.desc}, cardLink='add-password', svgPath='images/lock.svg')}"></div>
|
||||
|
||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.removePassword.title}, cardText=#{home.removePassword.desc}, cardLink='remove-password', svgPath='images/unlock.svg')}"></div>
|
||||
|
||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.compressPdfs.title}, cardText=#{home.compressPdfs.desc}, cardLink='compress-pdf', svgPath='images/file-zip.svg')}"></div>
|
||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.changeMetadata.title}, cardText=#{home.changeMetadata.desc}, cardLink='change-metadata', svgPath='images/clipboard-data.svg')}"></div>
|
||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.permissions.title}, cardText=#{home.permissions.desc}, cardLink='change-permissions', svgPath='images/shield-lock.svg')}"></div>
|
||||
|
||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.permissions.title}, cardText=#{home.permissions.desc}, cardLink='change-permissions', svgPath='images/shield-lock.svg')}"></div>
|
||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.ocr.title}, cardText=#{home.ocr.desc}, cardLink='ocr-pdf', svgPath='images/search.svg')}"></div>
|
||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.extractImages.title}, cardText=#{home.extractImages.desc}, cardLink='extract-images', svgPath='images/images.svg')}"></div>
|
||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.pdfToPDFA.title}, cardText=#{home.pdfToPDFA.desc}, cardLink='pdf-to-pdfa', svgPath='images/file-earmark-pdf.svg')}"></div>
|
||||
|
||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.pdfToPDFA.title}, cardText=#{home.pdfToPDFA.desc}, cardLink='pdf-to-pdfa', svgPath='images/file-earmark-pdf.svg')}"></div>
|
||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.PDFToWord.title}, cardText=#{home.PDFToWord.desc}, cardLink='pdf-to-word', svgPath='images/file-earmark-word.svg')}"></div>
|
||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.PDFToPresentation.title}, cardText=#{home.PDFToPresentation.desc}, cardLink='pdf-to-presentation', svgPath='images/file-earmark-ppt.svg')}"></div>
|
||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.PDFToText.title}, cardText=#{home.PDFToText.desc}, cardLink='pdf-to-text', svgPath='images/filetype-txt.svg')}"></div>
|
||||
|
||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.PDFToText.title}, cardText=#{home.PDFToText.desc}, cardLink='pdf-to-text', svgPath='images/filetype-txt.svg')}"></div>
|
||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.PDFToHTML.title}, cardText=#{home.PDFToHTML.desc}, cardLink='pdf-to-html', svgPath='images/filetype-html.svg')}"></div>
|
||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.PDFToXML.title}, cardText=#{home.PDFToXML.desc}, cardLink='pdf-to-xml', svgPath='images/filetype-xml.svg')}"></div>
|
||||
|
||||
|
||||
<div th:replace="~{fragments/card :: card(cardTitle=#{home.ScannerImageSplit.title}, cardText=#{home.ScannerImageSplit.desc}, cardLink='extract-image-scans', svgPath='images/scanner.svg')}"></div>
|
||||
|
||||
|
||||
|
||||
|
||||
32
src/main/resources/templates/other/adjust-contrast.html
Normal file
32
src/main/resources/templates/other/adjust-contrast.html
Normal file
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
|
||||
|
||||
<th:block th:insert="~{fragments/common :: head(title=#{extractImages.title})}"></th:block>
|
||||
|
||||
|
||||
<body>
|
||||
<div id="page-container">
|
||||
<div id="content-wrap">
|
||||
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
||||
<br> <br>
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<h2 th:text="#{extractImages.header}"></h2>
|
||||
|
||||
<form id="multiPdfForm" th:action="@{adjust-contrast}" method="post" enctype="multipart/form-data">
|
||||
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div>
|
||||
<div class="form-group">
|
||||
<label for="contrastRange">Contrast</label>
|
||||
<input name="contrastRange" type="range" class="form-control-range" id="contrastRange" min="-100" max="100" value="0" step="1">
|
||||
</div>
|
||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{extractImages.submit}"></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
54
src/main/resources/templates/other/extract-image-scans.html
Normal file
54
src/main/resources/templates/other/extract-image-scans.html
Normal file
@@ -0,0 +1,54 @@
|
||||
<!DOCTYPE html>
|
||||
<html th:lang="${#locale.toString()}" th:lang-direction="#{language.direction}" xmlns:th="http://www.thymeleaf.org">
|
||||
|
||||
<th:block th:insert="~{fragments/common :: head(title=#{home.ScannerImageSplit.title})}"></th:block>
|
||||
|
||||
|
||||
<body>
|
||||
<div id="page-container">
|
||||
<div id="content-wrap">
|
||||
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
||||
<br> <br>
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<h2 th:text="#{home.ScannerImageSplit.title}"></h2>
|
||||
|
||||
<form id="multiPdfForm" th:action="@{extract-image-scans}" method="post" enctype="multipart/form-data">
|
||||
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='image/*, application/pdf')}"></div>
|
||||
<div class="form-group">
|
||||
<label for="angleThreshold" th:text="#{ScannerImageSplit.selectText.1}"></label>
|
||||
<input type="number" class="form-control" id="angleThreshold" name="angle_threshold" value="5">
|
||||
<small id="angleThresholdHelp" class="form-text text-muted" th:text="#{ScannerImageSplit.selectText.2}"></small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="tolerance" th:text="#{ScannerImageSplit.selectText.3}"></label>
|
||||
<input type="number" class="form-control" id="tolerance" name="tolerance" value="20">
|
||||
<small id="toleranceHelp" class="form-text text-muted" th:text="#{ScannerImageSplit.selectText.4}"></small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="minArea" th:text="#{ScannerImageSplit.selectText.5}"></label>
|
||||
<input type="number" class="form-control" id="minArea" name="min_area" value="8000">
|
||||
<small id="minAreaHelp" class="form-text text-muted" th:text="#{ScannerImageSplit.selectText.6}"></small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="minContourArea" th:text="#{ScannerImageSplit.selectText.7}"></label>
|
||||
<input type="number" class="form-control" id="minContourArea" name="min_contour_area" value="500">
|
||||
<small id="minContourAreaHelp" class="form-text text-muted" th:text="#{ScannerImageSplit.selectText.8}"></small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="borderSize" th:text="#{ScannerImageSplit.selectText.9}"></label>
|
||||
<input type="number" class="form-control" id="borderSize" name="border_size" value="1">
|
||||
<small id="borderSizeHelp" class="form-text text-muted" th:text="#{ScannerImageSplit.selectText.10}"></small>
|
||||
</div>
|
||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{genericSubmit}"></button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -20,9 +20,9 @@
|
||||
<label for="languages" class="form-label" th:text="#{ocr.selectText.1}"></label>
|
||||
<hr>
|
||||
<div id="languages">
|
||||
<div th:each="language, iterStat : ${languages}" >
|
||||
<input type="checkbox" class="form-check-input" th:name="languages" th:value="${language}" th:id="${'language-' + language}" />
|
||||
<label class="form-check-label" th:for="${'language-' + language}" th:text=" ${language}"></label>
|
||||
<div th:each="language, iterStat : ${languages}">
|
||||
<input type="checkbox" th:name="languages" th:value="${language}" th:id="${'language-' + language}" />
|
||||
<label class="form-check-label" th:for="${'language-' + language}" th:text="${(language == 'eng') ? 'English' : language}"></label>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
@@ -53,6 +53,19 @@
|
||||
<input type="checkbox" class="form-check-input" name="clean-final" id="clean-final" />
|
||||
<label class="form-check-label" for="clean-final" th:text="#{ocr.selectText.5}"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input" name="removeImagesAfter" id="removeImagesAfter" />
|
||||
<label class="form-check-label" for="removeImagesAfter" th:text="#{ocr.selectText.11}"></label>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label th:text="#{ocr.selectText.12}"></label>
|
||||
<select class="form-control" name="ocrRenderType">
|
||||
<option value="hocr">HOCR (Latin/Roman alphabet only)</option>
|
||||
<option value="sandwich">Sandwich</option>
|
||||
</select>
|
||||
</div>
|
||||
<br>
|
||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{ocr.submit}"></button>
|
||||
</form>
|
||||
|
||||
Reference in New Issue
Block a user