Merge branch 'bug/remember-me' of

git@github.com:Stirling-Tools/Stirling-PDF.git into bug/remember-me
This commit is contained in:
Anthony Stirling
2024-11-15 21:26:51 +00:00
81 changed files with 1754 additions and 1889 deletions

View File

@@ -117,7 +117,6 @@ public class EndpointConfiguration {
addEndpointToGroup("Convert", "img-to-pdf");
addEndpointToGroup("Convert", "pdf-to-pdfa");
addEndpointToGroup("Convert", "file-to-pdf");
addEndpointToGroup("Convert", "xlsx-to-pdf");
addEndpointToGroup("Convert", "pdf-to-word");
addEndpointToGroup("Convert", "pdf-to-presentation");
addEndpointToGroup("Convert", "pdf-to-text");
@@ -163,7 +162,6 @@ public class EndpointConfiguration {
addEndpointToGroup("CLI", "repair");
addEndpointToGroup("CLI", "pdf-to-pdfa");
addEndpointToGroup("CLI", "file-to-pdf");
addEndpointToGroup("CLI", "xlsx-to-pdf");
addEndpointToGroup("CLI", "pdf-to-word");
addEndpointToGroup("CLI", "pdf-to-presentation");
addEndpointToGroup("CLI", "pdf-to-html");
@@ -184,6 +182,7 @@ public class EndpointConfiguration {
addEndpointToGroup("Python", "html-to-pdf");
addEndpointToGroup("Python", "url-to-pdf");
addEndpointToGroup("Python", "pdf-to-img");
addEndpointToGroup("Python", "file-to-pdf");
// openCV
addEndpointToGroup("OpenCV", "extract-image-scans");
@@ -191,14 +190,15 @@ public class EndpointConfiguration {
// LibreOffice
addEndpointToGroup("LibreOffice", "repair");
addEndpointToGroup("LibreOffice", "file-to-pdf");
addEndpointToGroup("Unoconv", "file-to-pdf");
addEndpointToGroup("LibreOffice", "xlsx-to-pdf");
addEndpointToGroup("LibreOffice", "pdf-to-word");
addEndpointToGroup("LibreOffice", "pdf-to-presentation");
addEndpointToGroup("LibreOffice", "pdf-to-rtf");
addEndpointToGroup("LibreOffice", "pdf-to-html");
addEndpointToGroup("LibreOffice", "pdf-to-xml");
// Unoconv
addEndpointToGroup("Unoconv", "file-to-pdf");
// OCRmyPDF
addEndpointToGroup("OCRmyPDF", "compress-pdf");
addEndpointToGroup("OCRmyPDF", "pdf-to-pdfa");
@@ -251,6 +251,7 @@ public class EndpointConfiguration {
// Ghostscript dependent endpoints
addEndpointToGroup("Ghostscript", "compress-pdf");
addEndpointToGroup("Ghostscript", "pdf-to-pdfa");
addEndpointToGroup("Ghostscript", "repair");
// Weasyprint dependent endpoints
addEndpointToGroup("Weasyprint", "html-to-pdf");

View File

@@ -19,10 +19,12 @@ import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.interfaces.DatabaseBackupInterface;
import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticatedPrincipal;
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
import stirling.software.SPDF.controller.api.pipeline.UserServiceInterface;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.AuthenticationType;
import stirling.software.SPDF.model.Authority;
import stirling.software.SPDF.model.Role;
@@ -31,6 +33,7 @@ import stirling.software.SPDF.repository.AuthorityRepository;
import stirling.software.SPDF.repository.UserRepository;
@Service
@Slf4j
public class UserService implements UserServiceInterface {
@Autowired private UserRepository userRepository;
@@ -45,6 +48,8 @@ public class UserService implements UserServiceInterface {
@Autowired DatabaseBackupInterface databaseBackupHelper;
@Autowired ApplicationProperties applicationProperties;
// Handle OAUTH2 login and user auto creation.
public boolean processOAuth2PostLogin(String username, boolean autoCreateUser)
throws IllegalArgumentException, IOException {
@@ -299,7 +304,13 @@ public class UserService implements UserServiceInterface {
boolean isValidEmail =
username.matches(
"^(?=.{1,64}@)[A-Za-z0-9]+(\\.[A-Za-z0-9_+.-]+)*@[^-][A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$");
return isValidSimpleUsername || isValidEmail;
List<String> notAllowedUserList = new ArrayList<>();
notAllowedUserList.add("ALL_USERS".toLowerCase());
boolean notAllowedUser = notAllowedUserList.contains(username.toLowerCase());
return (isValidSimpleUsername || isValidEmail) && !notAllowedUser;
}
private String getInvalidUsernameMessage() {
@@ -354,6 +365,14 @@ public class UserService implements UserServiceInterface {
if (principal instanceof UserDetails) {
return ((UserDetails) principal).getUsername();
} else if (principal instanceof OAuth2User) {
return ((OAuth2User) principal)
.getAttribute(
applicationProperties.getSecurity().getOauth2().getUseAsUsername());
} else if (principal instanceof CustomSaml2AuthenticatedPrincipal) {
return ((CustomSaml2AuthenticatedPrincipal) principal).getName();
} else if (principal instanceof String) {
return (String) principal;
} else {
return principal.toString();
}

View File

@@ -13,12 +13,14 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.model.api.misc.ReplaceAndInvertColorRequest;
import stirling.software.SPDF.service.misc.ReplaceAndInvertColorService;
@RestController
@RequestMapping("/api/v1/misc")
@Tag(name = "Misc", description = "Miscellaneous APIs")
public class ReplaceAndInvertColorController {
private ReplaceAndInvertColorService replaceAndInvertColorService;

View File

@@ -187,18 +187,31 @@ public class WatermarkController {
float watermarkHeight = heightSpacer + fontSize * textLines.length;
float pageWidth = page.getMediaBox().getWidth();
float pageHeight = page.getMediaBox().getHeight();
int watermarkRows = (int) (pageHeight / watermarkHeight + 1);
int watermarkCols = (int) (pageWidth / watermarkWidth + 1);
// Calculating the new width and height depending on the angle.
float radians = (float) Math.toRadians(rotation);
float newWatermarkWidth =
(float)
(Math.abs(watermarkWidth * Math.cos(radians))
+ Math.abs(watermarkHeight * Math.sin(radians)));
float newWatermarkHeight =
(float)
(Math.abs(watermarkWidth * Math.sin(radians))
+ Math.abs(watermarkHeight * Math.cos(radians)));
// Calculating the number of rows and columns.
int watermarkRows = (int) (pageHeight / newWatermarkHeight + 1);
int watermarkCols = (int) (pageWidth / newWatermarkWidth + 1);
// Add the text watermark
for (int i = 0; i < watermarkRows; i++) {
for (int j = 0; j < watermarkCols; j++) {
for (int i = 0; i <= watermarkRows; i++) {
for (int j = 0; j <= watermarkCols; j++) {
contentStream.beginText();
contentStream.setTextMatrix(
Matrix.getRotateInstance(
(float) Math.toRadians(rotation),
j * watermarkWidth,
i * watermarkHeight));
j * newWatermarkWidth,
i * newWatermarkHeight));
for (int k = 0; k < textLines.length; ++k) {
contentStream.showText(textLines[k]);

View File

@@ -15,7 +15,7 @@ import stirling.software.SPDF.controller.api.pipeline.UserServiceInterface;
import stirling.software.SPDF.service.SignatureService;
@Controller
@RequestMapping("/api/v1/general/")
@RequestMapping("/api/v1/general")
public class SignatureController {
@Autowired private SignatureService signatureService;

View File

@@ -141,6 +141,7 @@ navbar.language=اللغات
navbar.settings=إعدادات
navbar.allTools=أدوات
navbar.multiTool=أدوات متعددة
navbar.search=Search
navbar.sections.organize=تنظيم
navbar.sections.convertTo=تحويل الى PDF
navbar.sections.convertFrom=تحويل من PDF
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(مثال: 1,3,2 أو 4-8,2,10-12 أو 2n-1)
multiTool.title=أداة متعددة PDF
multiTool.header=أداة متعددة PDF
multiTool.uploadPrompts=اسم الملف
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
#view pdf
viewPdf.title=عرض PDF
viewPdf.header=عرض PDF

View File

@@ -141,6 +141,7 @@ navbar.language=Езици
navbar.settings=Настройки
navbar.allTools=Инструменти
navbar.multiTool=Мулти инструменти
navbar.search=Search
navbar.sections.organize=Организирайте
navbar.sections.convertTo=Преобразуване в PDF
navbar.sections.convertFrom=Преобразуване от PDF
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(напр. 1,3,2 или 4-8,2,10-12 или 2n-1)
multiTool.title=PDF Мулти инструмент
multiTool.header=PDF Мулти инструмент
multiTool.uploadPrompts=Име на файл
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
#view pdf
viewPdf.title=Преглед на PDF
viewPdf.header=Преглед на PDF

File diff suppressed because it is too large Load Diff

View File

@@ -141,6 +141,7 @@ navbar.language=Jazyky
navbar.settings=Nastavení
navbar.allTools=Nástroje
navbar.multiTool=Multifunkční nástroje
navbar.search=Search
navbar.sections.organize=Organizovat
navbar.sections.convertTo=Převést do PDF
navbar.sections.convertFrom=Převést z PDF
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(např. 1,3,2 nebo 4-8,2,10-12 nebo 2n-1)
multiTool.title=Vícefunkční nástroj pro PDF
multiTool.header=Vícefunkční nástroj pro PDF
multiTool.uploadPrompts=Název souboru
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
#view pdf
viewPdf.title=Zobrazit PDF
viewPdf.header=Zobrazit PDF

View File

@@ -141,6 +141,7 @@ navbar.language=Sprog
navbar.settings=Indstillinger
navbar.allTools=Værktøjer
navbar.multiTool=Multi Værktøjer
navbar.search=Search
navbar.sections.organize=Organisér
navbar.sections.convertTo=Konvertér til PDF
navbar.sections.convertFrom=Konvertér fra PDF
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(f.eks. 1,3,2 eller 4-8,2,10-12 eller 2n-1)
multiTool.title=PDF Multi Værktøj
multiTool.header=PDF Multi Værktøj
multiTool.uploadPrompts=Filnavn
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
#view pdf
viewPdf.title=Se PDF
viewPdf.header=Se PDF

View File

@@ -141,6 +141,7 @@ navbar.language=Sprachen
navbar.settings=Einstellungen
navbar.allTools=Werkzeuge
navbar.multiTool=Multitools
navbar.search=Suche
navbar.sections.organize=Organisieren
navbar.sections.convertTo=In PDF konvertieren
navbar.sections.convertFrom=Konvertieren von PDF
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(z.B. 1,3,2 oder 4-8,2,10-12 oder 2n-1)
multiTool.title=PDF-Multitool
multiTool.header=PDF-Multitool
multiTool.uploadPrompts=Dateiname
multiTool.selectAll=Alle auswählen
multiTool.deselectAll=Auswahl aufheben
multiTool.selectPages=Seiten auswählen
multiTool.selectedPages=Ausgewählte Seiten
multiTool.page=Seite
multiTool.deleteSelected=Auswahl löschen
multiTool.downloadAll=Downloaden
multiTool.downloadSelected=Auswahl downloaden
#view pdf
viewPdf.title=PDF anzeigen
viewPdf.header=PDF anzeigen

View File

@@ -141,6 +141,7 @@ navbar.language=Γλώσσες
navbar.settings=Ρυθμίσεις
navbar.allTools=Εργαλεία
navbar.multiTool=Multi Tools
navbar.search=Search
navbar.sections.organize=Οργάνωση
navbar.sections.convertTo=Μετατροπή σε PDF
navbar.sections.convertFrom=Μετατροπή από PDF
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(π.χ. 1,3,2 ή 4-8,2,10-12 ή 2n-1)
multiTool.title=PDF Πολυεργαλείο
multiTool.header=PDF Πολυεργαλείο
multiTool.uploadPrompts=Όνομα αρχείου
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
#view pdf
viewPdf.title=Προβολή PDF
viewPdf.header=Προβολή PDF

View File

@@ -141,6 +141,7 @@ navbar.language=Languages
navbar.settings=Settings
navbar.allTools=Tools
navbar.multiTool=Multi Tool
navbar.search=Search
navbar.sections.organize=Organize
navbar.sections.convertTo=Convert to PDF
navbar.sections.convertFrom=Convert from PDF
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
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
#view pdf
viewPdf.title=View PDF
viewPdf.header=View PDF

View File

@@ -141,6 +141,7 @@ navbar.language=Languages
navbar.settings=Settings
navbar.allTools=Tools
navbar.multiTool=Multi Tool
navbar.search=Search
navbar.sections.organize=Organize
navbar.sections.convertTo=Convert to PDF
navbar.sections.convertFrom=Convert from PDF
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
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
#view pdf
viewPdf.title=View PDF
viewPdf.header=View PDF

View File

@@ -141,6 +141,7 @@ navbar.language=Idiomas
navbar.settings=Configuración
navbar.allTools=Herramientas
navbar.multiTool=Multi herramientas
navbar.search=Search
navbar.sections.organize=Organizar
navbar.sections.convertTo=Convertir a PDF
navbar.sections.convertFrom=Convertir desde PDF
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(por ej., 1,3,2 o 4-8,2,10-12 o 2n-1)
multiTool.title=Multi-herramienta PDF
multiTool.header=Multi-herramienta PDF
multiTool.uploadPrompts=Nombre del archivo
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
#view pdf
viewPdf.title=Ver PDF
viewPdf.header=Ver PDF

View File

@@ -141,6 +141,7 @@ navbar.language=Languages
navbar.settings=Ezarpenak
navbar.allTools=Tools
navbar.multiTool=Multi Tools
navbar.search=Search
navbar.sections.organize=Organize
navbar.sections.convertTo=Convert to PDF
navbar.sections.convertFrom=Convert from PDF
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
multiTool.title=PDF erabilera anitzeko tresna
multiTool.header=PDF erabilera anitzeko tresna
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
#view pdf
viewPdf.title=View PDF
viewPdf.header=View PDF

View File

@@ -141,6 +141,7 @@ navbar.language=Langues
navbar.settings=Paramètres
navbar.allTools=Outils
navbar.multiTool=Outils Multiples
navbar.search=Search
navbar.sections.organize=Organisation
navbar.sections.convertTo=Convertir en PDF
navbar.sections.convertFrom=Convertir depuis PDF
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(par exemple 1,3,2 ou 4-8,2,10-12 ou 2n-1)
multiTool.title=Outil multifonction PDF
multiTool.header=Outil multifonction PDF
multiTool.uploadPrompts=Nom du fichier
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
#view pdf
viewPdf.title=Visualiser un PDF
viewPdf.header=Visualiser un PDF

View File

@@ -141,6 +141,7 @@ navbar.language=Teangacha
navbar.settings=Socruithe
navbar.allTools=Uirlisí
navbar.multiTool=Uirlisí Il
navbar.search=Search
navbar.sections.organize=Eagraigh
navbar.sections.convertTo=Tiontaigh go PDF
navbar.sections.convertFrom=Tiontaigh ó PDF
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(m.sh. 1,3,2 nó 4-8,2,10-12 nó 2n-1)
multiTool.title=Il-uirlis PDF
multiTool.header=Il-uirlis PDF
multiTool.uploadPrompts=Ainm comhaid
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
#view pdf
viewPdf.title=Féach PDF
viewPdf.header=Féach PDF

View File

@@ -141,6 +141,7 @@ navbar.language=भाषा
navbar.settings=सेटिंग्स
navbar.allTools=साधन
navbar.multiTool=विभिन्न साधन
navbar.search=Search
navbar.sections.organize=संगठित करें
navbar.sections.convertTo=पीडीएफ में कनवर्ट करें
navbar.sections.convertFrom=पीडीएफ से कनवर्ट करें
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(जैसे 1,3,2 या 4-8,2,10-12 या 2n-1)
multiTool.title=पीडीएफ मल्टी टूल
multiTool.header=पीडीएफ मल्टी टूल
multiTool.uploadPrompts=फाइल का नाम
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
#view pdf
viewPdf.title=पीडीएफ देखें
viewPdf.header=पीडीएफ देखें

View File

@@ -141,6 +141,7 @@ navbar.language=Jezici
navbar.settings=Postavke
navbar.allTools=Alati
navbar.multiTool=Multi Tools (Alati)
navbar.search=Search
navbar.sections.organize=Organizirati
navbar.sections.convertTo=Pretvori u PDF
navbar.sections.convertFrom=Pretvori iz PDF
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(npr. 1,3,2 ili 4-8,2,10-12 ili 2n-1)
multiTool.title=PDF Višenamjenski alat
multiTool.header=PDF Višenamjenski alat
multiTool.uploadPrompts=Naziv datoteke
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
#view pdf
viewPdf.title=Pogledaj
viewPdf.header=Pogledaj PDF

View File

@@ -141,6 +141,7 @@ navbar.language=Nyelvek
navbar.settings=Beállítások
navbar.allTools=Eszközök
navbar.multiTool=Multi Tools
navbar.search=Search
navbar.sections.organize=Összeállítás
navbar.sections.convertTo=Átalakítás PDF-be
navbar.sections.convertFrom=PDF-ből átalakítás
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(pl.: 1,3,2 vagy 4-8,2,10-12 vagy 2n-1)
multiTool.title=PDF többfunkciós eszköz
multiTool.header=PDF többfunkciós eszköz
multiTool.uploadPrompts=Fájl neve
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
#view pdf
viewPdf.title=PDF megtekintése
viewPdf.header=PDF megtekintése

View File

@@ -141,6 +141,7 @@ navbar.language=Bahasa
navbar.settings=Pengaturan
navbar.allTools=Alat
navbar.multiTool=Alat Multi
navbar.search=Search
navbar.sections.organize=Atur
navbar.sections.convertTo=Konversi ke PDF
navbar.sections.convertFrom=Konversi dari PDF
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(misalnya 1,3,2 atau 4-8,2,10-12 atau 2n-1)
multiTool.title=Alat Multi PDF
multiTool.header=Alat Multi PDF
multiTool.uploadPrompts=Nama Berkas
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
#view pdf
viewPdf.title=Lihat PDF
viewPdf.header=Lihat PDF

View File

@@ -141,6 +141,7 @@ navbar.language=Lingue
navbar.settings=Impostazioni
navbar.allTools=Strumenti
navbar.multiTool=Strumenti multipli
navbar.search=Search
navbar.sections.organize=Organizza
navbar.sections.convertTo=Converti in PDF
navbar.sections.convertFrom=Converti da PDF
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(ad es. 1,3,2 o 4-8,2,10-12 o 2n-1)
multiTool.title=Multifunzione PDF
multiTool.header=Multifunzione PDF
multiTool.uploadPrompts=Nome file
multiTool.selectAll=Seleziona tutto
multiTool.deselectAll=Deseleziona tutto
multiTool.selectPages=Seleziona pagina
multiTool.selectedPages=Seleziona pagine
multiTool.page=Pagina
multiTool.deleteSelected=Elimina selezionata
multiTool.downloadAll=Esporta
multiTool.downloadSelected=Esporta selezionata
#view pdf
viewPdf.title=Visualizza PDF
viewPdf.header=Visualizza PDF

View File

@@ -141,6 +141,7 @@ navbar.language=言語
navbar.settings=設定
navbar.allTools=ツール
navbar.multiTool=マルチツール
navbar.search=Search
navbar.sections.organize=整理
navbar.sections.convertTo=PDFへ変換
navbar.sections.convertFrom=PDFから変換
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(例:1,3,2または4-8,2,10-12または2n-1)
multiTool.title=PDFマルチツール
multiTool.header=PDFマルチツール
multiTool.uploadPrompts=ファイル名
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
#view pdf
viewPdf.title=PDFを表示
viewPdf.header=PDFを表示

View File

@@ -141,6 +141,7 @@ navbar.language=언어
navbar.settings=설정
navbar.allTools=도구
navbar.multiTool=Multi Tools
navbar.search=Search
navbar.sections.organize=조직
navbar.sections.convertTo=PDF로 변환
navbar.sections.convertFrom=PDF에서 변환
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(예: 1,3,2 또는 4-8,2,10-12 또는 2n-1)
multiTool.title=PDF 멀티툴
multiTool.header=PDF 멀티툴
multiTool.uploadPrompts=파일 이름
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
#view pdf
viewPdf.title=PDF 뷰어
viewPdf.header=PDF 뷰어

View File

@@ -141,6 +141,7 @@ navbar.language=Talen
navbar.settings=Instellingen
navbar.allTools=Tools
navbar.multiTool=Multitools
navbar.search=Search
navbar.sections.organize=Organizeren
navbar.sections.convertTo=Converteren naar PDF
navbar.sections.convertFrom=Converteren van PDF
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(bijv. 1,3,2 of 4-8,2,10-12 of 2n-1)
multiTool.title=PDF Multitool
multiTool.header=PDF Multitool
multiTool.uploadPrompts=Bestandsnaam
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
#view pdf
viewPdf.title=PDF bekijken
viewPdf.header=PDF bekijken

View File

@@ -141,6 +141,7 @@ navbar.language=Språk
navbar.settings=Innstillinger
navbar.allTools=Verktøy
navbar.multiTool=Multi Verktøy
navbar.search=Search
navbar.sections.organize=Organisere
navbar.sections.convertTo=Konverter til PDF
navbar.sections.convertFrom=Konverter fra PDF
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(f.eks. 1,3,2 eller 4-8,2,10-12 eller 2n-1)
multiTool.title=PDF-multiverktøy
multiTool.header=PDF-multiverktøy
multiTool.uploadPrompts=Filnavn
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
#view pdf
viewPdf.title=Vis PDF
viewPdf.header=Vis PDF

View File

@@ -141,6 +141,7 @@ navbar.language=Języki
navbar.settings=Ustawienia
navbar.allTools=Narzędzia
navbar.multiTool=Narzędzie Wielofunkcyjne
navbar.search=Search
navbar.sections.organize=Organizuj
navbar.sections.convertTo=Przetwórz na PDF
navbar.sections.convertFrom=Przetwórz z PDF
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(przykład 1,3,2 lub 4-8,2,10-12 lub 2n-1)
multiTool.title=Narzędzie Wielofunkcyjne PDF
multiTool.header=Narzędzie Wielofunkcyjne PDF
multiTool.uploadPrompts=Nazwa pliku
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
#view pdf
viewPdf.title=Podejrzyj PDF
viewPdf.header=Podejrzyj PDF

View File

@@ -141,6 +141,7 @@ navbar.language=Idiomas
navbar.settings=Configurações
navbar.allTools=Ferramentas
navbar.multiTool=Multiferramentas
navbar.search=Search
navbar.sections.organize=Organizar
navbar.sections.convertTo=Converter para PDF
navbar.sections.convertFrom=Converter de PDF
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(por exemplo 1,3,2 ou 4-8,2,10-12 ou 2n-1)
multiTool.title=Multiferramenta de PDF
multiTool.header=Multiferramenta de PDF
multiTool.uploadPrompts=Nome do arquivo
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
#view pdf
viewPdf.title=Visualizar PDF
viewPdf.header=Visualizar PDF

View File

@@ -141,6 +141,7 @@ navbar.language=Idiomas
navbar.settings=Configurações
navbar.allTools=Ferramentas
navbar.multiTool=Multi Tools
navbar.search=Search
navbar.sections.organize=Organizar
navbar.sections.convertTo=Converter para PDF
navbar.sections.convertFrom=Converter de PDF
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(ex: 1,3,2 ou 4-8,2,10-12 ou 2n-1)
multiTool.title=Multiferramenta de PDF
multiTool.header=Multiferramenta de PDF
multiTool.uploadPrompts=Nome do Arquivo
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
#view pdf
viewPdf.title=Visualizar PDF
viewPdf.header=Visualizar PDF

View File

@@ -141,6 +141,7 @@ navbar.language=Limbi
navbar.settings=Setări
navbar.allTools=Instrumente
navbar.multiTool=Instrumente Multiple
navbar.search=Search
navbar.sections.organize=Organizează
navbar.sections.convertTo=Convertește în PDF
navbar.sections.convertFrom=Convertește din PDF
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(ex. 1,3,2 sau 4-8,2,10-12 sau 2n-1)
multiTool.title=Instrument PDF multiplu
multiTool.header=Instrument PDF multiplu
multiTool.uploadPrompts=Nume Fișier
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
#view pdf
viewPdf.title=Vizualizează PDF
viewPdf.header=Vizualizează PDF

View File

@@ -141,6 +141,7 @@ navbar.language=Языки
navbar.settings=Настройки
navbar.allTools=Конвейеры
navbar.multiTool=Multi Tools
navbar.search=Search
navbar.sections.organize=Организация
navbar.sections.convertTo=Перевести в PDF
navbar.sections.convertFrom=Перевести из PDF
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(например, 1,3,2 или 4-8,2,10-12 или 2n-1
multiTool.title=Мультиинструмент PDF
multiTool.header=Мультиинструмент PDF
multiTool.uploadPrompts=Имя файла
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
#view pdf
viewPdf.title=Просмотреть PDF
viewPdf.header=Просмотреть PDF

View File

@@ -141,6 +141,7 @@ navbar.language=Languages
navbar.settings=Nastavenia
navbar.allTools=Tools
navbar.multiTool=Multi Tools
navbar.search=Search
navbar.sections.organize=Organize
navbar.sections.convertTo=Convert to PDF
navbar.sections.convertFrom=Convert from PDF
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(napr. 1,3,2 alebo 4-8,2,10-12 alebo 2n-1)
multiTool.title=PDF Multi Nástroj
multiTool.header=PDF Multi Nástroj
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
#view pdf
viewPdf.title=Zobraziť PDF
viewPdf.header=Zobraziť PDF

View File

@@ -141,6 +141,7 @@ navbar.language=Languages
navbar.settings=Podešavanja
navbar.allTools=Tools
navbar.multiTool=Multi Tools
navbar.search=Search
navbar.sections.organize=Organize
navbar.sections.convertTo=Convert to PDF
navbar.sections.convertFrom=Convert from PDF
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1)
multiTool.title=PDF Multi Alatka
multiTool.header=PDF Multi Alatka
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
#view pdf
viewPdf.title=Prikaz
viewPdf.header=Prikaz PDF-a

View File

@@ -141,6 +141,7 @@ navbar.language=Språk
navbar.settings=Inställningar
navbar.allTools=Verktyg
navbar.multiTool=Multiverktyg
navbar.search=Search
navbar.sections.organize=Organisera
navbar.sections.convertTo=Konvertera till PDF
navbar.sections.convertFrom=Konvertera från PDF
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(t.ex. 1,3,2 eller 4-8,2,10-12 eller 2n-1)
multiTool.title=PDF-multiverktyg
multiTool.header=PDF Multi-verktyg
multiTool.uploadPrompts=Filnamn
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
#view pdf
viewPdf.title=Visa PDF
viewPdf.header=Visa PDF

View File

@@ -141,6 +141,7 @@ navbar.language=ภาษา
navbar.settings=การตั้งค่า
navbar.allTools=เครื่องมือทั้งหมด
navbar.multiTool=เครื่องมือหลายตัว
navbar.search=Search
navbar.sections.organize=จัดระเบียบ
navbar.sections.convertTo=แปลงเป็น PDF
navbar.sections.convertFrom=แปลงจาก PDF
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(เช่น 1,3,2 หรือ 4-8,2,10-12 หรื
multiTool.title=เครื่องมือ PDF หลายตัว
multiTool.header=เครื่องมือ PDF หลายตัว
multiTool.uploadPrompts=ชื่อไฟล์
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
#view pdf
viewPdf.title=ดู PDF
viewPdf.header=ดู PDF

View File

@@ -141,6 +141,7 @@ navbar.language=Diller
navbar.settings=Ayarlar
navbar.allTools=Araçlar
navbar.multiTool=Çoklu Araçlar
navbar.search=Search
navbar.sections.organize=Düzenle
navbar.sections.convertTo=PDF'ye dönüştür
navbar.sections.convertFrom=PDF'den dönüştür
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(örn. 1,3,2 veya 4-8,2,10-12 veya 2n-1)
multiTool.title=PDF Çoklu Araç
multiTool.header=PDF Çoklu Araç
multiTool.uploadPrompts=Dosya Adı
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
#view pdf
viewPdf.title=PDF Görüntüle
viewPdf.header=PDF Görüntüle

View File

@@ -141,6 +141,7 @@ navbar.language=Мови
navbar.settings=Налаштування
navbar.allTools=Інструменти
navbar.multiTool=Мультіінструмент
navbar.search=Search
navbar.sections.organize=Організувати
navbar.sections.convertTo=Конвертувати в PDF
navbar.sections.convertFrom=Конвертувати з PDF
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(наприклад, 1,3,2 або 4-8,2,10-12 або 2n
multiTool.title=Мультіінструмент PDF
multiTool.header=Мультіінструмент PDF
multiTool.uploadPrompts=Ім'я файлу
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
#view pdf
viewPdf.title=Переглянути PDF
viewPdf.header=Переглянути PDF

View File

@@ -141,6 +141,7 @@ navbar.language=Ngôn ngữ
navbar.settings=Cài đặt
navbar.allTools=Công cụ
navbar.multiTool=Đa công cụ
navbar.search=Search
navbar.sections.organize=Sắp xếp
navbar.sections.convertTo=Chuyển đổi sang PDF
navbar.sections.convertFrom=Chuyển đổi từ PDF
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(ví dụ: 1,3,2 hoặc 4-8,2,10-12 hoặc 2n-1)
multiTool.title=Công cụ đa năng PDF
multiTool.header=Công cụ đa năng PDF
multiTool.uploadPrompts=Tên tệp
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
#view pdf
viewPdf.title=Xem PDF
viewPdf.header=Xem PDF

View File

@@ -141,6 +141,7 @@ navbar.language=语言
navbar.settings=设置
navbar.allTools=工具箱
navbar.multiTool=多功能工具
navbar.search=Search
navbar.sections.organize=组织
navbar.sections.convertTo=转换成PDF
navbar.sections.convertFrom=从PDF转换
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=例如1,3,2 或 4-8,2,10-12 或 2n-1
multiTool.title=PDF多功能工具
multiTool.header=PDF多功能工具
multiTool.uploadPrompts=文件名
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
#view pdf
viewPdf.title=浏览PDF
viewPdf.header=浏览PDF

View File

@@ -141,6 +141,7 @@ navbar.language=語言
navbar.settings=設定
navbar.allTools=工具
navbar.multiTool=複合工具
navbar.search=Search
navbar.sections.organize=整理
navbar.sections.convertTo=轉換為 PDF
navbar.sections.convertFrom=從 PDF 轉換
@@ -933,7 +934,14 @@ pdfOrganiser.placeholder=(例如 1,3,2 或 4-8,2,10-12 或 2n-1
multiTool.title=PDF 複合工具
multiTool.header=PDF 複合工具
multiTool.uploadPrompts=檔名
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
#view pdf
viewPdf.title=檢視 PDF
viewPdf.header=檢視 PDF

View File

@@ -212,15 +212,81 @@ label {
.page-number {
position: absolute;
top: 5px;
right: 0px;
color: var(--md-sys-color-on-surface);
background-color: var(--md-sys-color-surface-5);
left: 5px;
color: var(--md-sys-color-on-secondary);
background-color: rgba(162, 201, 255, 0.8);
padding: 6px 8px;
border-radius: 8px;
font-size: 16px;
z-index: 2;
font-weight: 450;
}
.tool-header {
margin: 0.5rem 1rem 2rem;
}
#select-pages-button {
opacity: 0.5;
}
.selected-pages-container {
background-color: var(--md-sys-color-surface);
border-radius: 16px;
padding: 15px;
width: 100%;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
font-family: Arial, sans-serif;
}
.selected-pages-container h3 {
color: var(--md-sys-color-on-surface);
font-size: 1.2em;
margin-bottom: 10px;
}
.pages-list {
display: flex;
flex-wrap: wrap;
gap: 10px;
padding: 0;
list-style: none;
max-height: 10rem;
overflow: auto;
}
.page-item {
background-color: var(--md-sys-color-surface-container-low);
border-radius: 8px;
padding: 8px 12px;
display: flex;
align-items: center;
gap: 8px;
font-weight: bold;
color: var(--md-sys-color-on-surface);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
width: 7rem;
height: 2.5rem;
}
.selected-page-number {
width: 4rem;
font-size: small;
}
.remove-btn {
cursor: pointer;
color: var(--md-sys-color-on-surface);
font-size: 1.2em;
}
.checkbox-container {
align-items: center;
justify-content: center;
display: flex;
flex-direction: column;
}
.checkbox-label {
font-size: medium;
}

View File

@@ -126,3 +126,28 @@ html[dir="rtl"] .pdf-actions_container:last-child>.pdf-actions_insert-file-butto
aspect-ratio: 1;
border-radius: 100px;
}
.pdf-actions_checkbox {
position: absolute;
top: 5px;
right: 3px;
color: var(--md-sys-color-on-surface);
background-color: var(--md-sys-color-surface-5);
padding: 6px 8px;
border-radius: 8px;
font-size: 16px;
z-index: 2;
}
.hidden {
display: none;
}
.pdf-actions_insert-file-blank-button {
position: absolute;
top: 75%;
right: 50%;
translate: 0% -50%;
aspect-ratio: 1;
border-radius: 100px;
}

View File

@@ -5,6 +5,8 @@
src: url(../../fonts/google-symbol.woff2) format('woff2');
}
.material-symbols-rounded {
font-family: 'Material Symbols Rounded';
font-weight: 300;

View File

@@ -83,14 +83,16 @@ function syncFavorites() {
cards.forEach(card => {
const isFavorite = localStorage.getItem(card.id) === "favorite";
const starIcon = card.querySelector(".favorite-icon span.material-symbols-rounded");
if (isFavorite) {
starIcon.classList.remove("no-fill");
starIcon.classList.add("fill");
card.classList.add("favorite");
} else {
starIcon.classList.remove("fill");
starIcon.classList.add("no-fill");
card.classList.remove("favorite");
if (starIcon) {
if (isFavorite) {
starIcon.classList.remove("no-fill");
starIcon.classList.add("fill");
card.classList.add("favorite");
} else {
starIcon.classList.remove("fill");
starIcon.classList.add("no-fill");
card.classList.remove("favorite");
}
}
});
updateFavoritesSection();
@@ -260,4 +262,4 @@ document.addEventListener("DOMContentLoaded", function () {
}, 500);
showFavoritesOnly();
});
});

View File

@@ -1,6 +1,7 @@
class PdfActionsManager {
pageDirection;
pagesContainer;
static selectedPages = []; // Static property shared across all instances
constructor(id) {
this.pagesContainer = document.getElementById(id);
@@ -73,6 +74,11 @@ class PdfActionsManager {
this.addFiles(imgContainer);
}
insertFileBlankButtonCallback(e) {
var imgContainer = this.getPageContainer(e.target);
this.addFiles(imgContainer, true);
}
splitFileButtonCallback(e) {
var imgContainer = this.getPageContainer(e.target);
imgContainer.classList.toggle("split-before");
@@ -89,9 +95,11 @@ class PdfActionsManager {
this.rotateCWButtonCallback = this.rotateCWButtonCallback.bind(this);
this.deletePageButtonCallback = this.deletePageButtonCallback.bind(this);
this.insertFileButtonCallback = this.insertFileButtonCallback.bind(this);
this.insertFileBlankButtonCallback = this.insertFileBlankButtonCallback.bind(this);
this.splitFileButtonCallback = this.splitFileButtonCallback.bind(this);
}
adapt(div) {
div.classList.add("pdf-actions_container");
const leftDirection = this.pageDirection === "rtl" ? "right" : "left";
@@ -132,6 +140,45 @@ class PdfActionsManager {
div.appendChild(buttonContainer);
//enerate checkbox to select individual pages
const selectCheckbox = document.createElement("input");
selectCheckbox.type = "checkbox";
selectCheckbox.classList.add("pdf-actions_checkbox", "form-check-input");
selectCheckbox.id = `selectPageCheckbox`;
selectCheckbox.checked = window.selectAll;
div.appendChild(selectCheckbox);
//only show whenpage select mode is active
if (!window.selectPage) {
selectCheckbox.classList.add("hidden");
} else {
selectCheckbox.classList.remove("hidden");
}
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);
}
}
if (window.selectedPages.length > 0 && !window.selectPage) {
window.toggleSelectPageVisibility();
}
if (window.selectedPages.length == 0 && window.selectPage) {
window.toggleSelectPageVisibility();
}
window.updateSelectedPagesDisplay();
};
const insertFileButtonContainer = document.createElement("div");
insertFileButtonContainer.classList.add(
@@ -152,6 +199,12 @@ class PdfActionsManager {
splitFileButton.onclick = this.splitFileButtonCallback;
insertFileButtonContainer.appendChild(splitFileButton);
const insertFileBlankButton = document.createElement("button");
insertFileBlankButton.classList.add("btn", "btn-primary", "pdf-actions_insert-file-blank-button");
insertFileBlankButton.innerHTML = `<span class="material-symbols-rounded">insert_page_break</span>`;
insertFileBlankButton.onclick = this.insertFileBlankButtonCallback;
insertFileButtonContainer.appendChild(insertFileBlankButton);
div.appendChild(insertFileButtonContainer);
// add this button to every element, but only show it on the last one :D
@@ -179,15 +232,29 @@ class PdfActionsManager {
};
div.addEventListener("mouseenter", () => {
window.updatePageNumbersAndCheckboxes();
const pageNumber = Array.from(div.parentNode.children).indexOf(div) + 1;
adaptPageNumber(pageNumber, div);
const checkbox = document.getElementById(`selectPageCheckbox-${pageNumber}`);
if (checkbox && !window.selectPage) {
checkbox.classList.remove("hidden");
}
});
div.addEventListener("mouseleave", () => {
const pageNumber = Array.from(div.parentNode.children).indexOf(div) + 1;
const pageNumberElement = div.querySelector(".page-number");
if (pageNumberElement) {
div.removeChild(pageNumberElement);
}
const checkbox = document.getElementById(`selectPageCheckbox-${pageNumber}`);
if (checkbox && !window.selectPage) {
checkbox.classList.add("hidden");
}
});
document.addEventListener("selectedPagesUpdated", () => {
window.updateSelectedPagesDisplay();
});
return div;

View File

@@ -22,6 +22,12 @@ class PdfContainer {
this.nameAndArchiveFiles = this.nameAndArchiveFiles.bind(this);
this.splitPDF = this.splitPDF.bind(this);
this.splitAll = this.splitAll.bind(this);
this.deleteSelected = this.deleteSelected.bind(this);
this.toggleSelectAll = this.toggleSelectAll.bind(this);
this.updateSelectedPagesDisplay = this.updateSelectedPagesDisplay.bind(this);
this.toggleSelectPageVisibility = this.toggleSelectPageVisibility.bind(this);
this.updatePagesFromCSV = this.updatePagesFromCSV.bind(this);
this.addFilesBlankAll = this.addFilesBlankAll.bind(this)
this.pdfAdapters = pdfAdapters;
@@ -31,6 +37,7 @@ class PdfContainer {
addFiles: this.addFiles,
rotateElement: this.rotateElement,
updateFilename: this.updateFilename,
deleteSelected: this.deleteSelected,
});
});
@@ -38,6 +45,14 @@ class PdfContainer {
window.exportPdf = this.exportPdf;
window.rotateAll = this.rotateAll;
window.splitAll = this.splitAll;
window.deleteSelected = this.deleteSelected;
window.toggleSelectAll = this.toggleSelectAll;
window.updateSelectedPagesDisplay = this.updateSelectedPagesDisplay;
window.toggleSelectPageVisibility = this.toggleSelectPageVisibility;
window.updatePagesFromCSV = this.updatePagesFromCSV;
window.updateSelectedPagesDisplay = this.updateSelectedPagesDisplay;
window.updatePageNumbersAndCheckboxes = this.updatePageNumbersAndCheckboxes;
window.addFilesBlankAll = this.addFilesBlankAll
const filenameInput = document.getElementById("filename-input");
const downloadBtn = document.getElementById("export-button");
@@ -77,19 +92,27 @@ class PdfContainer {
}
}
addFiles(nextSiblingElement) {
var input = document.createElement("input");
input.type = "file";
input.multiple = true;
input.setAttribute("accept", "application/pdf,image/*");
input.onchange = async (e) => {
const files = e.target.files;
addFiles(nextSiblingElement, blank = false) {
if (blank) {
this.addFilesFromFiles(files, nextSiblingElement);
this.updateFilename(files ? files[0].name : "");
};
this.addFilesBlank(nextSiblingElement);
input.click();
} else {
var input = document.createElement("input");
input.type = "file";
input.multiple = true;
input.setAttribute("accept", "application/pdf,image/*");
input.onchange = async (e) => {
const files = e.target.files;
this.addFilesFromFiles(files, nextSiblingElement);
this.updateFilename(files ? files[0].name : "");
const selectAll = document.getElementById("select-pages-container");
selectAll.classList.toggle("hidden", false);
};
input.click();
}
}
async addFilesFromFiles(files, nextSiblingElement) {
@@ -108,6 +131,47 @@ 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);
}
rotateElement(element, deg) {
var lastTransform = element.style.rotate;
if (!lastTransform) {
@@ -215,28 +279,246 @@ class PdfContainer {
}
rotateAll(deg) {
for (var i = 0; i < this.pagesContainer.childNodes.length; i++) {
for (let i = 0; i < this.pagesContainer.childNodes.length; i++) {
const child = this.pagesContainer.children[i];
if (!child) continue;
const pageIndex = i + 1;
//if in page select mode is active rotate only selected pages
if (window.selectPage && !window.selectedPages.includes(pageIndex)) continue;
const img = child.querySelector("img");
if (!img) continue;
this.rotateElement(img, deg);
}
}
deleteSelected() {
window.selectedPages.sort((a, b) => a - b);
let deletions = 0;
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"));
}
toggleSelectAll() {
const checkboxes = document.querySelectorAll(".pdf-actions_checkbox");
window.selectAll = !window.selectAll;
const selectIcon = document.getElementById("select-icon");
const deselectIcon = document.getElementById("deselect-icon");
if (selectIcon.style.display === "none") {
selectIcon.style.display = "inline";
deselectIcon.style.display = "none";
} else {
selectIcon.style.display = "none";
deselectIcon.style.display = "inline";
}
checkboxes.forEach((checkbox) => {
checkbox.checked = window.selectAll;
const pageNumber = Array.from(checkbox.parentNode.parentNode.children).indexOf(checkbox.parentNode) + 1;
if (checkbox.checked) {
if (!window.selectedPages.includes(pageNumber)) {
window.selectedPages.push(pageNumber);
}
} else {
const index = window.selectedPages.indexOf(pageNumber);
if (index !== -1) {
window.selectedPages.splice(index, 1);
}
}
});
this.updateSelectedPagesDisplay();
}
parseCSVInput(csvInput, maxPageIndex) {
const pages = new Set();
csvInput.split(",").forEach((item) => {
const range = item.split("-").map((p) => parseInt(p.trim()));
if (range.length === 2) {
const [start, end] = range;
for (let i = start; i <= end && i <= maxPageIndex; i++) {
if (i > 0) { // Ensure the page number is greater than 0
pages.add(i);
}
}
} else if (range.length === 1 && Number.isInteger(range[0])) {
const page = range[0];
if (page > 0 && page <= maxPageIndex) { // Ensure page is within valid range
pages.add(page);
}
}
});
return Array.from(pages).sort((a, b) => a - b);
}
updatePagesFromCSV() {
const csvInput = document.getElementById("csv-input").value;
const allPages = this.pagesContainer.querySelectorAll(".page-container");
const maxPageIndex = allPages.length;
window.selectedPages = this.parseCSVInput(csvInput, maxPageIndex);
this.updateSelectedPagesDisplay();
const allCheckboxes = document.querySelectorAll(".pdf-actions_checkbox");
allCheckboxes.forEach((checkbox) => {
const page = parseInt(checkbox.getAttribute("data-page-number"));
checkbox.checked = window.selectedPages.includes(page);
});
}
formatSelectedPages(pages) {
if (pages.length === 0) return "";
pages.sort((a, b) => a - b); // Sort the page numbers in ascending order
const ranges = [];
let start = pages[0];
let end = start;
for (let i = 1; i < pages.length; i++) {
if (pages[i] === end + 1) {
// Consecutive page, update end
end = pages[i];
} else {
// Non-consecutive page, finalize current range
ranges.push(start === end ? `${start}` : `${start}-${end}`);
start = pages[i];
end = start;
}
}
// Add the last range
ranges.push(start === end ? `${start}` : `${start}-${end}`);
return ranges.join(", ");
}
updateSelectedPagesDisplay() {
const selectedPagesList = document.getElementById("selected-pages-list");
const selectedPagesInput = document.getElementById("csv-input");
selectedPagesList.innerHTML = ""; // Clear the list
window.selectedPages.forEach((page) => {
const pageItem = document.createElement("div");
pageItem.className = "page-item";
const pageNumber = document.createElement("span");
const pagelabel = /*[[#{multiTool.page}]]*/ 'Page';
pageNumber.className = "selected-page-number";
pageNumber.innerText = `${pagelabel} ${page}`;
pageItem.appendChild(pageNumber);
const removeBtn = document.createElement("span");
removeBtn.className = "remove-btn";
removeBtn.innerHTML = "✕";
// Remove page from selected pages list and update display and checkbox
removeBtn.onclick = () => {
window.selectedPages = window.selectedPages.filter((p) => p !== page);
this.updateSelectedPagesDisplay();
const checkbox = document.getElementById(`selectPageCheckbox-${page}`);
if (checkbox) {
checkbox.checked = false;
}
};
pageItem.appendChild(removeBtn);
selectedPagesList.appendChild(pageItem);
});
// Update the input field with the formatted page list
selectedPagesInput.value = this.formatSelectedPages(window.selectedPages);
}
parsePageRanges(ranges) {
const pages = new Set();
ranges.split(',').forEach(range => {
const [start, end] = range.split('-').map(Number);
if (end) {
for (let i = start; i <= end; i++) {
pages.add(i);
}
} else {
pages.add(start);
}
});
return Array.from(pages).sort((a, b) => a - b);
}
addFilesBlankAll() {
const allPages = this.pagesContainer.querySelectorAll(".page-container");
allPages.forEach((page, index) => {
if (index !== 0) {
this.addFiles(page, true)
}
});
}
splitAll() {
const allPages = this.pagesContainer.querySelectorAll(".page-container");
if (this.pagesContainer.querySelectorAll(".split-before").length > 0) {
allPages.forEach(page => {
page.classList.remove("split-before");
});
} else {
allPages.forEach(page => {
page.classList.add("split-before");
});
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");
}
});
}
async splitPDF(baseDocBytes, splitters) {
const baseDocument = await PDFLib.PDFDocument.load(baseDocBytes);
const pageNum = baseDocument.getPages().length;
@@ -279,52 +561,54 @@ class PdfContainer {
return zip;
}
async exportPdf() {
async exportPdf(selected) {
const pdfDoc = await PDFLib.PDFDocument.create();
const pageContainers = this.pagesContainer.querySelectorAll(".page-container"); // Select all .page-container elements
for (var i = 0; i < pageContainers.length; i++) {
const img = pageContainers[i].querySelector("img"); // Find the img element within each .page-container
if (!img) continue;
let page;
if (img.doc) {
const pages = await pdfDoc.copyPages(img.doc, [img.pageIdx]);
page = pages[0];
pdfDoc.addPage(page);
} else {
page = pdfDoc.addPage([img.naturalWidth, img.naturalHeight]);
const imageBytes = await fetch(img.src).then((res) => res.arrayBuffer());
const uint8Array = new Uint8Array(imageBytes);
const imageType = detectImageType(uint8Array);
if (!selected || window.selectedPages.includes(i + 1)) {
const img = pageContainers[i].querySelector("img"); // Find the img element within each .page-container
if (!img) continue;
let page;
if (img.doc) {
const pages = await pdfDoc.copyPages(img.doc, [img.pageIdx]);
page = pages[0];
pdfDoc.addPage(page);
} else {
page = pdfDoc.addPage([img.naturalWidth, img.naturalHeight]);
const imageBytes = await fetch(img.src).then((res) => res.arrayBuffer());
const uint8Array = new Uint8Array(imageBytes);
const imageType = detectImageType(uint8Array);
let image;
switch (imageType) {
case 'PNG':
image = await pdfDoc.embedPng(imageBytes);
break;
case 'JPEG':
image = await pdfDoc.embedJpg(imageBytes);
break;
case 'TIFF':
image = await pdfDoc.embedTiff(imageBytes);
break;
case 'GIF':
console.warn(`Unsupported image type: ${imageType}`);
continue; // Skip this image
default:
console.warn(`Unsupported image type: ${imageType}`);
continue; // Skip this image
let image;
switch (imageType) {
case 'PNG':
image = await pdfDoc.embedPng(imageBytes);
break;
case 'JPEG':
image = await pdfDoc.embedJpg(imageBytes);
break;
case 'TIFF':
image = await pdfDoc.embedTiff(imageBytes);
break;
case 'GIF':
console.warn(`Unsupported image type: ${imageType}`);
continue; // Skip this image
default:
console.warn(`Unsupported image type: ${imageType}`);
continue; // Skip this image
}
page.drawImage(image, {
x: 0,
y: 0,
width: img.naturalWidth,
height: img.naturalHeight,
});
}
const rotation = img.style.rotate;
if (rotation) {
const rotationAngle = parseInt(rotation.replace(/[^\d-]/g, ""));
page.setRotation(PDFLib.degrees(page.getRotation().angle + rotationAngle));
}
page.drawImage(image, {
x: 0,
y: 0,
width: img.naturalWidth,
height: img.naturalHeight,
});
}
const rotation = img.style.rotate;
if (rotation) {
const rotationAngle = parseInt(rotation.replace(/[^\d-]/g, ""));
page.setRotation(PDFLib.degrees(page.getRotation().angle + rotationAngle));
}
}
pdfDoc.setCreator(stirlingPDFLabel);
@@ -436,7 +720,44 @@ class PdfContainer {
// filenameInput.value.replace('.','');
// }
}
toggleSelectPageVisibility() {
window.selectPage = !window.selectPage;
const checkboxes = document.querySelectorAll(".pdf-actions_checkbox");
checkboxes.forEach(checkbox => {
checkbox.classList.toggle("hidden", !window.selectPage);
});
const deleteButton = document.getElementById("delete-button");
deleteButton.classList.toggle("hidden", !window.selectPage);
const selectedPages = document.getElementById("selected-pages-display");
selectedPages.classList.toggle("hidden", !window.selectPage);
const selectAll = document.getElementById("select-All-Container");
selectedPages.classList.toggle("hidden", !window.selectPage);
const exportSelected = document.getElementById("export-selected-button");
exportSelected.classList.toggle("hidden", !window.selectPage);
const selectPagesButton = document.getElementById("select-pages-button");
selectPagesButton.style.opacity = window.selectPage ? "1" : "0.5";
if (window.selectPage) {
this.updatePageNumbersAndCheckboxes();
}
}
updatePageNumbersAndCheckboxes() {
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);
});
}
}
function detectImageType(uint8Array) {
// Check for PNG signature
if (uint8Array[0] === 137 && uint8Array[1] === 80 && uint8Array[2] === 78 && uint8Array[3] === 71) {
@@ -450,7 +771,7 @@ function detectImageType(uint8Array) {
// Check for TIFF signature (little-endian and big-endian)
if ((uint8Array[0] === 73 && uint8Array[1] === 73 && uint8Array[2] === 42 && uint8Array[3] === 0) ||
(uint8Array[0] === 77 && uint8Array[1] === 77 && uint8Array[2] === 0 && uint8Array[3] === 42)) {
(uint8Array[0] === 77 && uint8Array[1] === 77 && uint8Array[2] === 0 && uint8Array[3] === 42)) {
return 'TIFF';
}
@@ -461,4 +782,7 @@ function detectImageType(uint8Array) {
return 'UNKNOWN';
}
export default PdfContainer;

View File

@@ -18,7 +18,7 @@
<span class="tool-header-text" th:text="#{MarkdownToPDF.header}"></span>
</div>
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/convert/markdown/pdf'}">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='text/markdown')}"></div>
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='.md')}"></div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{MarkdownToPDF.submit}"></button>
</form>
<p class="mt-3" th:text="#{MarkdownToPDF.help}"></p>

View File

@@ -24,16 +24,20 @@
<input id="height" type="hidden" name="height">
<button type="submit" class="btn btn-primary" th:text="#{crop.submit}"></button>
</form>
<div style="position: relative; display: inline-block;">
<canvas id="crop-pdf-canvas" style="position: absolute; top: 0; left: 0; z-index: 1;"></canvas>
<canvas id="overlayCanvas" style="position: absolute; top: 0; left: 0; z-index: 2;"></canvas>
<div id="canvasesContainer" style="position: relative; margin: 20px 0; width: auto;">
<canvas id="cropPdfCanvas" style="width: 100%"></canvas>
<canvas id="overlayCanvas" style="position: absolute; top: 0; left: 0; z-index: 2; width: 100%"></canvas>
</div>
</div>
</div>
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
<script>
let pdfCanvas = document.getElementById('crop-pdf-canvas');
let pdfCanvas = document.getElementById('cropPdfCanvas');
let overlayCanvas = document.getElementById('overlayCanvas');
let canvasesContainer = document.getElementById('canvasesContainer');
canvasesContainer.style.display = "none";
let containerRect = canvasesContainer.getBoundingClientRect();
let context = pdfCanvas.getContext('2d');
let overlayContext = overlayCanvas.getContext('2d');
@@ -59,8 +63,11 @@
let rectWidth = 0;
let rectHeight = 0;
fileInput.addEventListener('change', function(e) {
let file = e.target.files[0];
let pageScale = 1; // The scale which the pdf page renders
let timeId = null; // timeout id for resizing canvases event
function renderPageFromFile(file) {
if (file.type === 'application/pdf') {
let reader = new FileReader();
reader.onload = function(ev) {
@@ -74,6 +81,35 @@
};
reader.readAsArrayBuffer(file);
}
}
window.addEventListener("resize", function() {
clearTimeout(timeId);
timeId = setTimeout(function () {
if (fileInput.files.length == 0) return;
let canvasesContainer = document.getElementById('canvasesContainer');
let containerRect = canvasesContainer.getBoundingClientRect();
context.clearRect(0, 0, pdfCanvas.width, pdfCanvas.height);
overlayContext.clearRect(0, 0, overlayCanvas.width, overlayCanvas.height);
pdfCanvas.width = containerRect.width;
pdfCanvas.height = containerRect.height;
overlayCanvas.width = containerRect.width;
overlayCanvas.height = containerRect.height;
let file = fileInput.files[0];
renderPageFromFile(file);
}, 1000);
});
fileInput.addEventListener('change', function(e) {
canvasesContainer.style.display = "block"; // set for visual purposes
let file = e.target.files[0];
renderPageFromFile(file);
});
cropForm.addEventListener('submit', function(e) {
@@ -81,8 +117,8 @@
// Ορίστε συντεταγμένες για ολόκληρη την επιφάνεια του PDF
xInput.value = 0;
yInput.value = 0;
widthInput.value = pdfCanvas.width;
heightInput.value = pdfCanvas.height;
widthInput.value = containerRect.width;
heightInput.value = containerRect.height;
}
});
@@ -117,10 +153,10 @@
let flippedY = pdfCanvas.height - e.offsetY;
xInput.value = startX;
yInput.value = flippedY;
widthInput.value = rectWidth;
heightInput.value = rectHeight;
xInput.value = startX / pageScale;
yInput.value = flippedY / pageScale;
widthInput.value = rectWidth / pageScale;
heightInput.value = rectHeight /pageScale;
// Draw the final rectangle on the main canvas
context.strokeStyle = 'red';
@@ -131,7 +167,16 @@
function renderPage(pageNumber) {
pdfDoc.getPage(pageNumber).then(function(page) {
let viewport = page.getViewport({ scale: 1.0 });
let canvasesContainer = document.getElementById('canvasesContainer');
let containerRect = canvasesContainer.getBoundingClientRect();
pageScale = containerRect.width / page.getViewport({ scale: 1 }).width; // The new scale
let viewport = page.getViewport({ scale: containerRect.width / page.getViewport({ scale: 1 }).width });
canvasesContainer.width =viewport.width;
canvasesContainer.height = viewport.height;
pdfCanvas.width = viewport.width;
pdfCanvas.height = viewport.height;

View File

@@ -9,8 +9,10 @@
<script th:inline="javascript">
const currentVersion = /*[[${@appVersion}]]*/ '';
const noFavourites = /*[[#{noFavourites}]]*/ '';
const updateAvailable = /*[[#{settings.updateAvailable}]]*/ '';
console.log(noFavourites);
const updateAvailable = /*[[#{settings.updateAvailable}]]*/ '';
</script>
<script th:src="@{'/js/homecard.js'}"></script>
<script th:src="@{'/js/githubVersion.js'}"></script>
<nav class="navbar navbar-expand-xl">
<div class="container ">
@@ -308,10 +310,10 @@
</li> -->
</ul>
<ul class="navbar-nav flex-nowrap">
<ul class="navbar-nav flex-nowrap">
<li class="nav-item dropdown">
<a class="nav-link" id="navbarDropdown-5" href="#" role="button" data-bs-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
aria-haspopup="true" aria-expanded="false" th:title="#{navbar.favorite}">
<span class="material-symbols-rounded">
star
</span>
@@ -324,7 +326,7 @@
</div>
</li>
<li class="nav-item">
<a class="nav-link" id="dark-mode-toggle" href="#">
<a class="nav-link" id="dark-mode-toggle" href="#" th:title="#{navbar.darkmode}">
<span class="material-symbols-rounded" id="dark-mode-icon">
dark_mode
</span>
@@ -333,7 +335,7 @@
</li>
<li class="nav-item dropdown">
<a class="nav-link" href="#" id="languageDropdown" role="button" data-bs-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
aria-haspopup="true" aria-expanded="false" th:title="#{navbar.language}">
<span class="material-symbols-rounded">
language
</span>
@@ -349,7 +351,7 @@
</li>
<li class="nav-item dropdown">
<a class="nav-link" href="#" id="searchDropdown" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<a class="nav-link" href="#" id="searchDropdown" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" th:title="#{navbar.search}">
<span class="material-symbols-rounded">
search
</span>
@@ -358,7 +360,7 @@
<div class="dropdown-menu dropdown-menu-tp" aria-labelledby="searchDropdown">
<div class="dropdown-menu-wrapper px-xl-2 px-2">
<form th:action="@{''}" class="d-flex p-2 search-form" id="searchForm">
<input class="form-control search-input" type="search" placeholder="Search" aria-label="Search" id="navbarSearchInput">
<input class="form-control search-input" type="search" th:placeholder="#{navbar.search}" aria-label="Search" id="navbarSearchInput">
</form>
<!-- Search Results -->
<div id="searchResults" class="search-results scrollable-y dropdown-mw-20"></div>
@@ -368,13 +370,13 @@
<li class="nav-item" th:if="${!@runningEE}">
<a href="https://stirlingpdf.com/pricing" class="nav-link go-pro-link" target="_blank" rel="noopener noreferrer">
<span class="go-pro-badge" th:text="#{enterpriseEdition.button}"></span>
<span class="go-pro-badge" th:text="#{enterpriseEdition.button}"></span>
</a>
</li>
<li class="nav-item">
<!-- Settings Button -->
<a href="#" class="nav-link" data-bs-toggle="modal" data-bs-target="#settingsModal">
<a href="#" class="nav-link" data-bs-toggle="modal" data-bs-target="#settingsModal" th:title="#{navbar.settings}">
<span class="material-symbols-rounded">
settings
</span>
@@ -405,14 +407,14 @@
<p class="mb-0" th:utext="#{settings.appVersion} + ' ' + ${@appVersion}"></p>
<div class="d-flex justify-content-between align-items-center mb-3 mt-3">
<div class="footer-center" style="flex-direction: row;">
<a href="https://github.com/Stirling-Tools/Stirling-PDF" class="mx-1" role="button"
<a href="https://github.com/Stirling-Tools/Stirling-PDF" class="mx-1" role="button" target="_blank"
th:title="#{visitGithub}">
<img th:src="@{'/images/github.svg'}" alt="github">
</a>
<a href="https://hub.docker.com/r/frooodle/s-pdf" class="mx-1" role="button" th:title="#{seeDockerHub}">
<a href="https://hub.docker.com/r/frooodle/s-pdf" class="mx-1" role="button" target="_blank"th:title="#{seeDockerHub}">
<img th:src="@{'/images/docker.svg'}" alt="docker">
</a>
<a href="https://discord.gg/Cn8pWhQRxZ" class="mx-1" role="button" th:title="#{joinDiscord}">
<a href="https://discord.gg/Cn8pWhQRxZ" class="mx-1" role="button" target="_blank" th:title="#{joinDiscord}">
<img th:src="@{'/images/discord.svg'}" alt="discord">
</a>
</div>

View File

@@ -47,13 +47,53 @@
cut
</span>
</button>
<button id="export-button" class="btn btn-primary enable-on-file" onclick="exportPdf()" disabled>
<button id="select-pages-container" class="btn btn-secondary enable-on-file"
th:title="#{multiTool.selectPages}" onclick="toggleSelectPageVisibility()" disabled>
<span id="select-pages-button" class="material-symbols-rounded">
event_list
</span>
</button>
<button id="select-All-Container" class="btn btn-secondary enable-on-file hidden"
onclick="toggleSelectAll()" disabled>
<span th:title="#{multiTool.selectAll}" class="material-symbols-rounded"
id="select-icon">select_all</span>
<span th:title="#{multiTool.deselectAll}" class="material-symbols-rounded" style="display: none;"
id="deselect-icon">deselect</span>
</button>
<div class=" button-container">
<button th:title="#{multiTool.deleteSelected}" id="delete-button" class="btn btn-danger hidden"
onclick="deleteSelected()">
<span class="material-symbols-rounded">delete</span>
</button>
</div>
<button id="export-selected-button" class="btn btn-primary enable-on-file hidden"
onclick="exportPdf(true)" disabled>
<span th:title="#{multiTool.downloadSelected}" class="material-symbols-rounded">
file_save
</span>
</button>
<button class="btn btn-secondary enable-on-file" onclick="addFilesBlankAll()" disabled>
<span class="material-symbols-rounded">
insert_page_break
</span>
</button>
<button id="export-button" class="btn btn-primary enable-on-file" onclick="exportPdf(false)" disabled>
<span th:title="#{multiTool.downloadAll}" class="material-symbols-rounded">
download
</span>
</button>
</div>
<div id="selected-pages-display" class="selected-pages-container hidden">
<div style="display:flex; height:3rem; margin-right:1rem">
<h5 th:text="#{multiTool.selectedPages}" style="white-space: nowrap; margin-right: 1rem;">Selected
Pages</h5>
<input type="text" id="csv-input" class="form-control" style="height:2.5rem" placeholder="1,3,5-10"
value="">
</div>
<ul id="selected-pages-list" class="pages-list"></ul>
</div>
</div>
<div class="multi-tool-container">
<div class="d-flex flex-wrap" id="pages-container-wrapper">
<div id="pages-container">
@@ -77,6 +117,20 @@
</div>
<script type="module" th:src="@{'/pdfjs-legacy/pdf.mjs'}"></script>
<script th:src="@{'/js/thirdParty/pdf-lib.min.js'}"></script>
<script>
window.selectedPages = [];
window.selectPage = false;
window.selectAll = false;
const csvInput = document.getElementById("csv-input");
csvInput.addEventListener("keydown", function (event) {
if (event.key === "Enter") {
updatePagesFromCSV();
}
});
csvInput.addEventListener("blur", function () {
updatePagesFromCSV();
});
</script>
<script type="module">
import PdfContainer from './js/multitool/PdfContainer.js';
import DragDropManager from "./js/multitool/DragDropManager.js";
@@ -90,7 +144,6 @@
// enables the default action buttons on each file
const pdfActionsManager = new PdfActionsManager('pages-container');
const fileDragManager = new FileDragManager();
// Scroll the wrapper horizontally
// Automatically exposes rotateAll, addFiles and exportPdf to the window for the global buttons.

View File

@@ -30,10 +30,8 @@
<!-- Button to download the JSON -->
<a href="#" id="downloadJson" class="btn btn-primary mt-3" style="display: none;" th:text="#{getPdfInfo.downloadJson}">Download JSON</a>
</div>
<script th:src="@{'/js/fetch-utils.js'}"></script>
<script>
import { fetchWithCsrf } from 'js/fetch-utils.js';
document.getElementById("pdfInfoForm").addEventListener("submit", function(event) {
event.preventDefault();
@@ -156,4 +154,4 @@
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>
</html>

View File

@@ -31,11 +31,13 @@
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" id="includeMetadata" name="includeMetadata">
<label class="form-check-label" for="includeMetadata" th:text="#{splitByChapters.includeMetadata}"></label>
<input type="hidden" name="includeMetadata" value="false" />
</div>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" id="allowDuplicates" name="allowDuplicates">
<label class="form-check-label" for="allowDuplicates" th:text="#{splitByChapters.allowDuplicates}"></label>
<input type="hidden" name="allowDuplicates" value="false" />
</div>
<p>

View File

@@ -184,7 +184,7 @@ See https://github.com/adobe-type-tools/cmap-resources
<div id="secondaryToolbar" class="secondaryToolbar hidden doorHangerRight">
<div id="secondaryToolbarButtonContainer">
<button id="secondaryOpenFile" class="secondaryToolbarButton" hidden="true" title="Open File" tabindex="51" data-l10n-id="pdfjs-open-file-button">
<button id="secondaryOpenFile" class="secondaryToolbarButton visibleMediumView" title="Open File" tabindex="51" data-l10n-id="pdfjs-open-file-button">
<span data-l10n-id="pdfjs-open-file-button-label">Open</span>
</button>