Compare commits
21 Commits
Frooodle/a
...
Frooodle/f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ea179e3e6 | ||
|
|
7ccb4d59b0 | ||
|
|
d9309563d6 | ||
|
|
9da7dd45c1 | ||
|
|
93e51e47ff | ||
|
|
1fe5db3611 | ||
|
|
d9d39570a2 | ||
|
|
9a49a554b1 | ||
|
|
f2573fd297 | ||
|
|
f05a3927fd | ||
|
|
fbf97c486b | ||
|
|
87ca994ec6 | ||
|
|
d72009dddb | ||
|
|
6944df435f | ||
|
|
60ba90b32d | ||
|
|
c650a766a9 | ||
|
|
d5b0f1f4ab | ||
|
|
81dbfa220f | ||
|
|
86cbde4ec2 | ||
|
|
ff519adebb | ||
|
|
03887cc9f9 |
3
.github/pull_request_template.md
vendored
3
.github/pull_request_template.md
vendored
@@ -4,9 +4,10 @@ Please provide a summary of the changes, including relevant motivation and conte
|
||||
|
||||
Closes #(issue_number)
|
||||
|
||||
## Checklist:
|
||||
## Checklist
|
||||
|
||||
- [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
|
||||
- [ ] I have performed a self-review of my own code
|
||||
- [ ] I have commented my code, particularly in hard-to-understand areas
|
||||
- [ ] My changes generate no new warnings
|
||||
- [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<p align="center"><img src="https://raw.githubusercontent.com/Stirling-Tools/Stirling-PDF/main/docs/stirling.png" width="80" ><br><h1 align="center">Stirling-PDF</h1>
|
||||
</p>
|
||||
|
||||
|
||||
# How to add new languages to Stirling-PDF
|
||||
|
||||
Fork Stirling-PDF and make a new branch out of Main
|
||||
@@ -14,13 +13,14 @@ https://github.com/Stirling-Tools/Stirling-PDF/tree/main/src/main/resources/stat
|
||||
Any SVG flags are fine, i got most of mine from [here](https://flagicons.lipis.dev/)
|
||||
If your language isn't represented by a flag just find whichever closely matches it, such as for Arabic i chose Saudi Arabia
|
||||
|
||||
|
||||
For example to add Polish you would add
|
||||
|
||||
```html
|
||||
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="pl_PL">
|
||||
<img src="images/flags/pl.svg" alt="icon" width="20" height="15"> Polski
|
||||
</a>
|
||||
```
|
||||
|
||||
The data-language-code is the code used to reference the file in the next step.
|
||||
|
||||
Start by copying the existing english property file
|
||||
@@ -29,7 +29,6 @@ Start by copying the existing english property file
|
||||
|
||||
Copy and rename it to messages_{your data-language-code here}.properties, in the polish example you would set the name to messages_pl_PL.properties
|
||||
|
||||
|
||||
Then simply translate all property entries within that file and make a PR into main for others to use!
|
||||
|
||||
If you do not have a java IDE i am happy to verify the changes worked once you raise PR (but won't be able to verify the translations themselves)
|
||||
@@ -48,4 +47,10 @@ ignore = [
|
||||
]
|
||||
```
|
||||
|
||||
## Add New Translation Tags
|
||||
|
||||
- **Important**: If you add any new translation tags, they must first be added to the `messages_en_GB.properties` file. This ensures consistency across all language files.
|
||||
- New translation tags **must be added** to the `messages_en_GB.properties` file to maintain a reference for other languages.
|
||||
- After adding the new tags to `messages_en_GB.properties`, add and translate them in the respective language file (e.g., `messages_pl_PL.properties`).
|
||||
|
||||
Make sure to place the entry under the correct language section. This helps maintain the accuracy of translation progress statistics and ensures that the translation tool or scripts do not misinterpret the completion rate.
|
||||
|
||||
20
README.md
20
README.md
@@ -170,42 +170,42 @@ Stirling PDF currently supports 38!
|
||||
|
||||
| Language | Progress |
|
||||
| ------------------------------------------- | -------------------------------------- |
|
||||
| Arabic (العربية) (ar_AR) |  |
|
||||
| Arabic (العربية) (ar_AR) |  |
|
||||
| Basque (Euskara) (eu_ES) |  |
|
||||
| Bulgarian (Български) (bg_BG) |  |
|
||||
| Catalan (Català) (ca_CA) |  |
|
||||
| Croatian (Hrvatski) (hr_HR) |  |
|
||||
| Czech (Česky) (cs_CZ) |  |
|
||||
| Danish (Dansk) (da_DK) |  |
|
||||
| Dutch (Nederlands) (nl_NL) |  |
|
||||
| Danish (Dansk) (da_DK) |  |
|
||||
| Dutch (Nederlands) (nl_NL) |  |
|
||||
| English (English) (en_GB) |  |
|
||||
| English (US) (en_US) |  |
|
||||
| French (Français) (fr_FR) |  |
|
||||
| German (Deutsch) (de_DE) |  |
|
||||
| Greek (Ελληνικά) (el_GR) |  |
|
||||
| Hindi (हिंदी) (hi_IN) |  |
|
||||
| Hungarian (Magyar) (hu_HU) |  |
|
||||
| Hindi (हिंदी) (hi_IN) |  |
|
||||
| Hungarian (Magyar) (hu_HU) |  |
|
||||
| Indonesia (Bahasa Indonesia) (id_ID) |  |
|
||||
| Irish (Gaeilge) (ga_IE) |  |
|
||||
| Italian (Italiano) (it_IT) |  |
|
||||
| Japanese (日本語) (ja_JP) |  |
|
||||
| Korean (한국어) (ko_KR) |  |
|
||||
| Norwegian (Norsk) (no_NB) |  |
|
||||
| Norwegian (Norsk) (no_NB) |  |
|
||||
| Polish (Polski) (pl_PL) |  |
|
||||
| Portuguese (Português) (pt_PT) |  |
|
||||
| Portuguese Brazilian (Português) (pt_BR) |  |
|
||||
| Romanian (Română) (ro_RO) |  |
|
||||
| Romanian (Română) (ro_RO) |  |
|
||||
| Russian (Русский) (ru_RU) |  |
|
||||
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) |  |
|
||||
| Simplified Chinese (简体中文) (zh_CN) |  |
|
||||
| Slovakian (Slovensky) (sk_SK) |  |
|
||||
| Spanish (Español) (es_ES) |  |
|
||||
| Swedish (Svenska) (sv_SE) |  |
|
||||
| Swedish (Svenska) (sv_SE) |  |
|
||||
| Thai (ไทย) (th_TH) |  |
|
||||
| Traditional Chinese (繁體中文) (zh_TW) |  |
|
||||
| Turkish (Türkçe) (tr_TR) |  |
|
||||
| Turkish (Türkçe) (tr_TR) |  |
|
||||
| Ukrainian (Українська) (uk_UA) |  |
|
||||
| Vietnamese (Tiếng Việt) (vi_VN) |  |
|
||||
| Vietnamese (Tiếng Việt) (vi_VN) |  |
|
||||
|
||||
## Contributing (creating issues, translations, fixing bugs, etc.)
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ public class SPdfApplication {
|
||||
|
||||
@Autowired private Environment env;
|
||||
|
||||
|
||||
@Autowired ApplicationProperties applicationProperties;
|
||||
|
||||
private static String serverPortStatic;
|
||||
|
||||
@@ -45,7 +45,15 @@ public class CustomAuthenticationSuccessHandler
|
||||
? (SavedRequest) session.getAttribute("SPRING_SECURITY_SAVED_REQUEST")
|
||||
: null;
|
||||
|
||||
if (savedRequest != null
|
||||
// Check for the stored previous page URL
|
||||
String previousPageUrl = (session != null) ? (String) session.getAttribute("PREVIOUS_PAGE_URL") : null;
|
||||
|
||||
if (previousPageUrl != null) {
|
||||
// Redirect to the stored previous page URL
|
||||
getRedirectStrategy().sendRedirect(request, response, previousPageUrl);
|
||||
// Remove the stored previous page URL from the session
|
||||
session.removeAttribute("PREVIOUS_PAGE_URL");
|
||||
} else if (savedRequest != null
|
||||
&& !RequestUriUtils.isStaticResource(
|
||||
request.getContextPath(), savedRequest.getRedirectUrl())) {
|
||||
// Redirect to the original destination
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package stirling.software.SPDF.config.security;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
@@ -13,6 +17,7 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import stirling.software.SPDF.config.DatabaseBackupInterface;
|
||||
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
|
||||
import stirling.software.SPDF.controller.api.pipeline.UserServiceInterface;
|
||||
@@ -23,10 +28,6 @@ import stirling.software.SPDF.model.User;
|
||||
import stirling.software.SPDF.repository.AuthorityRepository;
|
||||
import stirling.software.SPDF.repository.UserRepository;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class UserService implements UserServiceInterface {
|
||||
|
||||
|
||||
@@ -0,0 +1,299 @@
|
||||
package stirling.software.SPDF.controller.api;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import org.apache.pdfbox.Loader;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDDocumentOutline;
|
||||
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import stirling.software.SPDF.model.PdfMetadata;
|
||||
import stirling.software.SPDF.model.api.SplitPdfByChaptersRequest;
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/general")
|
||||
@Tag(name = "General", description = "General APIs")
|
||||
public class SplitPdfByChaptersController {
|
||||
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(SplitPdfByChaptersController.class);
|
||||
|
||||
@PostMapping(value = "/split-pdf-by-chapters", consumes = "multipart/form-data")
|
||||
@Operation(
|
||||
summary = "Split PDFs by Chapters",
|
||||
description = "Splits a PDF into chapters and returns a ZIP file.")
|
||||
public ResponseEntity<byte[]> splitPdf(@ModelAttribute SplitPdfByChaptersRequest request)
|
||||
throws Exception {
|
||||
MultipartFile file = request.getFileInput();
|
||||
boolean includeMetadata = request.getIncludeMetadata();
|
||||
Integer bookmarkLevel =
|
||||
request.getBookmarkLevel(); // levels start from 0 (top most bookmarks)
|
||||
if (bookmarkLevel < 0) {
|
||||
return ResponseEntity.badRequest().body("Invalid bookmark level".getBytes());
|
||||
}
|
||||
PDDocument sourceDocument = Loader.loadPDF(file.getBytes());
|
||||
|
||||
// checks if the document is encrypted by an empty user password
|
||||
if (sourceDocument.isEncrypted()) {
|
||||
try {
|
||||
sourceDocument.setAllSecurityToBeRemoved(true);
|
||||
logger.info("Removing security from the source document ");
|
||||
} catch (Exception e) {
|
||||
logger.warn("Cannot decrypt the pdf");
|
||||
}
|
||||
}
|
||||
PDDocumentOutline outline = sourceDocument.getDocumentCatalog().getDocumentOutline();
|
||||
|
||||
if (outline == null) {
|
||||
logger.warn("No outline found for {}", file.getOriginalFilename());
|
||||
return ResponseEntity.badRequest().body("No outline found".getBytes());
|
||||
}
|
||||
List<Bookmark> bookmarks = new ArrayList<>();
|
||||
try {
|
||||
bookmarks =
|
||||
extractOutlineItems(
|
||||
sourceDocument,
|
||||
outline.getFirstChild(),
|
||||
bookmarks,
|
||||
outline.getFirstChild().getNextSibling(),
|
||||
0,
|
||||
bookmarkLevel);
|
||||
// to handle last page edge case
|
||||
bookmarks.get(bookmarks.size() - 1).setEndPage(sourceDocument.getNumberOfPages());
|
||||
Bookmark lastBookmark = bookmarks.get(bookmarks.size() - 1);
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("Unable to extract outline items", e);
|
||||
return ResponseEntity.internalServerError()
|
||||
.body("Unable to extract outline items".getBytes());
|
||||
}
|
||||
|
||||
boolean allowDuplicates = request.getAllowDuplicates();
|
||||
if (!allowDuplicates) {
|
||||
/*
|
||||
duplicates are generated when multiple bookmarks correspond to the same page,
|
||||
if the user doesn't want duplicates mergeBookmarksThatCorrespondToSamePage() method will merge the titles of all
|
||||
the bookmarks that correspond to the same page, and treat them as a single bookmark
|
||||
*/
|
||||
bookmarks = mergeBookmarksThatCorrespondToSamePage(bookmarks);
|
||||
}
|
||||
for (Bookmark bookmark : bookmarks) {
|
||||
logger.info(
|
||||
"{}::::{} to {}",
|
||||
bookmark.getTitle(),
|
||||
bookmark.getStartPage(),
|
||||
bookmark.getEndPage());
|
||||
}
|
||||
List<ByteArrayOutputStream> splitDocumentsBoas =
|
||||
getSplitDocumentsBoas(sourceDocument, bookmarks, includeMetadata);
|
||||
|
||||
Path zipFile = createZipFile(bookmarks, splitDocumentsBoas);
|
||||
|
||||
byte[] data = Files.readAllBytes(zipFile);
|
||||
Files.deleteIfExists(zipFile);
|
||||
|
||||
String filename =
|
||||
Filenames.toSimpleFileName(file.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "");
|
||||
sourceDocument.close();
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM);
|
||||
}
|
||||
|
||||
private List<Bookmark> mergeBookmarksThatCorrespondToSamePage(List<Bookmark> bookmarks) {
|
||||
String mergedTitle = "";
|
||||
List<Bookmark> chaptersToBeRemoved = new ArrayList<>();
|
||||
for (Bookmark bookmark : bookmarks) {
|
||||
if (bookmark.getStartPage() == bookmark.getEndPage()) {
|
||||
mergedTitle = mergedTitle.concat(bookmark.getTitle().concat(" "));
|
||||
chaptersToBeRemoved.add(bookmark);
|
||||
} else {
|
||||
if (!mergedTitle.isEmpty()) {
|
||||
if (mergedTitle.length() > 255) {
|
||||
mergedTitle = mergedTitle.substring(0, 253) + "...";
|
||||
}
|
||||
|
||||
bookmarks.set(
|
||||
bookmarks.indexOf(bookmark),
|
||||
new Bookmark(
|
||||
mergedTitle, bookmark.getStartPage(), bookmark.getEndPage()));
|
||||
}
|
||||
mergedTitle = "";
|
||||
}
|
||||
}
|
||||
bookmarks.removeAll(chaptersToBeRemoved);
|
||||
return bookmarks;
|
||||
}
|
||||
|
||||
private static List<Bookmark> extractOutlineItems(
|
||||
PDDocument sourceDocument,
|
||||
PDOutlineItem current,
|
||||
List<Bookmark> bookmarks,
|
||||
PDOutlineItem nextParent,
|
||||
int level,
|
||||
int maxLevel)
|
||||
throws Exception {
|
||||
|
||||
while (current != null) {
|
||||
|
||||
String currentTitle = current.getTitle().replace("/", "");
|
||||
int firstPage =
|
||||
sourceDocument.getPages().indexOf(current.findDestinationPage(sourceDocument));
|
||||
PDOutlineItem child = current.getFirstChild();
|
||||
PDOutlineItem nextSibling = current.getNextSibling();
|
||||
int endPage;
|
||||
if (child != null && level < maxLevel) {
|
||||
endPage =
|
||||
sourceDocument
|
||||
.getPages()
|
||||
.indexOf(child.findDestinationPage(sourceDocument));
|
||||
} else if (nextSibling != null) {
|
||||
endPage =
|
||||
sourceDocument
|
||||
.getPages()
|
||||
.indexOf(nextSibling.findDestinationPage(sourceDocument));
|
||||
} else if (nextParent != null) {
|
||||
|
||||
endPage =
|
||||
sourceDocument
|
||||
.getPages()
|
||||
.indexOf(nextParent.findDestinationPage(sourceDocument));
|
||||
} else {
|
||||
endPage = -2;
|
||||
/*
|
||||
happens when we have something like this:
|
||||
Outline Item 2
|
||||
Outline Item 2.1
|
||||
Outline Item 2.1.1
|
||||
Outline Item 2.2
|
||||
Outline 2.2.1
|
||||
Outline 2.2.2 <--- this item neither has an immediate next parent nor an immediate next sibling
|
||||
Outline Item 3
|
||||
*/
|
||||
}
|
||||
if (!bookmarks.isEmpty()
|
||||
&& bookmarks.get(bookmarks.size() - 1).getEndPage() == -2
|
||||
&& firstPage
|
||||
>= bookmarks
|
||||
.get(bookmarks.size() - 1)
|
||||
.getStartPage()) { // for handling the above-mentioned case
|
||||
Bookmark previousBookmark = bookmarks.get(bookmarks.size() - 1);
|
||||
previousBookmark.setEndPage(firstPage);
|
||||
}
|
||||
bookmarks.add(new Bookmark(currentTitle, firstPage, endPage));
|
||||
|
||||
// Recursively process children
|
||||
if (child != null && level < maxLevel) {
|
||||
extractOutlineItems(
|
||||
sourceDocument, child, bookmarks, nextSibling, level + 1, maxLevel);
|
||||
}
|
||||
|
||||
current = nextSibling;
|
||||
}
|
||||
return bookmarks;
|
||||
}
|
||||
|
||||
private Path createZipFile(
|
||||
List<Bookmark> bookmarks, List<ByteArrayOutputStream> splitDocumentsBoas)
|
||||
throws Exception {
|
||||
Path zipFile = Files.createTempFile("split_documents", ".zip");
|
||||
String fileNumberFormatter = "%0" + (Integer.toString(bookmarks.size()).length()) + "d ";
|
||||
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
|
||||
for (int i = 0; i < splitDocumentsBoas.size(); i++) {
|
||||
|
||||
// split files will be named as "[FILE_NUMBER] [BOOKMARK_TITLE].pdf"
|
||||
|
||||
String fileName =
|
||||
String.format(fileNumberFormatter, i)
|
||||
+ bookmarks.get(i).getTitle()
|
||||
+ ".pdf";
|
||||
ByteArrayOutputStream baos = splitDocumentsBoas.get(i);
|
||||
byte[] pdf = baos.toByteArray();
|
||||
|
||||
ZipEntry pdfEntry = new ZipEntry(fileName);
|
||||
zipOut.putNextEntry(pdfEntry);
|
||||
zipOut.write(pdf);
|
||||
zipOut.closeEntry();
|
||||
|
||||
logger.info("Wrote split document {} to zip file", fileName);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed writing to zip", e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
logger.info("Successfully created zip file with split documents: {}", zipFile);
|
||||
return zipFile;
|
||||
}
|
||||
|
||||
public List<ByteArrayOutputStream> getSplitDocumentsBoas(
|
||||
PDDocument sourceDocument, List<Bookmark> bookmarks, boolean includeMetadata)
|
||||
throws Exception {
|
||||
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>();
|
||||
PdfMetadata metadata = null;
|
||||
if (includeMetadata) {
|
||||
metadata = PdfUtils.extractMetadataFromPdf(sourceDocument);
|
||||
}
|
||||
for (Bookmark bookmark : bookmarks) {
|
||||
try (PDDocument splitDocument = new PDDocument()) {
|
||||
boolean isSinglePage = (bookmark.getStartPage() == bookmark.getEndPage());
|
||||
|
||||
for (int i = bookmark.getStartPage();
|
||||
i < bookmark.getEndPage() + (isSinglePage ? 1 : 0);
|
||||
i++) {
|
||||
PDPage page = sourceDocument.getPage(i);
|
||||
splitDocument.addPage(page);
|
||||
logger.info("Adding page {} to split document", i);
|
||||
}
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
if (includeMetadata) {
|
||||
PdfUtils.setMetadataToPdf(splitDocument, metadata);
|
||||
}
|
||||
|
||||
splitDocument.save(baos);
|
||||
|
||||
splitDocumentsBoas.add(baos);
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed splitting documents and saving them", e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return splitDocumentsBoas;
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
class Bookmark {
|
||||
private String title;
|
||||
private int startPage;
|
||||
private int endPage;
|
||||
}
|
||||
@@ -43,6 +43,7 @@ public class PageNumbersController {
|
||||
"This operation takes an input PDF file and adds page numbers to it. Input:PDF Output:PDF Type:SISO")
|
||||
public ResponseEntity<byte[]> addPageNumbers(@ModelAttribute AddPageNumbersRequest request)
|
||||
throws IOException {
|
||||
|
||||
MultipartFile file = request.getFileInput();
|
||||
String customMargin = request.getCustomMargin();
|
||||
int position = request.getPosition();
|
||||
@@ -52,7 +53,8 @@ public class PageNumbersController {
|
||||
int pageNumber = startingNumber;
|
||||
byte[] fileBytes = file.getBytes();
|
||||
PDDocument document = Loader.loadPDF(fileBytes);
|
||||
|
||||
float font_size = request.getFontSize();
|
||||
String font_type = request.getFontType();
|
||||
float marginFactor;
|
||||
switch (customMargin.toLowerCase()) {
|
||||
case "small":
|
||||
@@ -73,7 +75,7 @@ public class PageNumbersController {
|
||||
break;
|
||||
}
|
||||
|
||||
float fontSize = 12.0f;
|
||||
float fontSize = font_size;
|
||||
if (pagesToNumber == null || pagesToNumber.length() == 0) {
|
||||
pagesToNumber = "all";
|
||||
}
|
||||
@@ -131,7 +133,20 @@ public class PageNumbersController {
|
||||
new PDPageContentStream(
|
||||
document, page, PDPageContentStream.AppendMode.APPEND, true, true);
|
||||
contentStream.beginText();
|
||||
contentStream.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA), fontSize);
|
||||
switch (font_type.toLowerCase()) {
|
||||
case "helvetica":
|
||||
contentStream.setFont(
|
||||
new PDType1Font(Standard14Fonts.FontName.HELVETICA), fontSize);
|
||||
break;
|
||||
case "courier":
|
||||
contentStream.setFont(
|
||||
new PDType1Font(Standard14Fonts.FontName.COURIER), fontSize);
|
||||
break;
|
||||
case "times":
|
||||
contentStream.setFont(
|
||||
new PDType1Font(Standard14Fonts.FontName.TIMES_ROMAN), fontSize);
|
||||
break;
|
||||
}
|
||||
contentStream.newLineAtOffset(x, y);
|
||||
contentStream.showText(text);
|
||||
contentStream.endText();
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package stirling.software.SPDF.controller.web;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.Authentication;
|
||||
@@ -13,6 +13,15 @@ import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
|
||||
import stirling.software.SPDF.model.*;
|
||||
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
|
||||
@@ -22,11 +31,6 @@ import stirling.software.SPDF.model.provider.GoogleProvider;
|
||||
import stirling.software.SPDF.model.provider.KeycloakProvider;
|
||||
import stirling.software.SPDF.repository.UserRepository;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Controller
|
||||
@Slf4j
|
||||
@Tag(name = "Account Security", description = "Account Security APIs")
|
||||
@@ -46,6 +50,13 @@ public class AccountWebController {
|
||||
return "redirect:/";
|
||||
}
|
||||
|
||||
// Store the previous page URL in the session before redirecting to the login page
|
||||
HttpSession session = request.getSession();
|
||||
String referer = request.getHeader("Referer");
|
||||
if (referer != null && !referer.contains("/login")) {
|
||||
session.setAttribute("PREVIOUS_PAGE_URL", referer);
|
||||
}
|
||||
|
||||
Map<String, String> providerList = new HashMap<>();
|
||||
|
||||
OAUTH2 oauth = applicationProperties.getSecurity().getOAUTH2();
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package stirling.software.SPDF.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@@ -9,6 +7,8 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
@Entity
|
||||
@Table(name = "users")
|
||||
public class User implements Serializable {
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package stirling.software.SPDF.model.api;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class SplitPdfByChaptersRequest extends PDFFile {
|
||||
@Schema(description = "Whether to include Metadata or not", example = "true")
|
||||
private Boolean includeMetadata;
|
||||
|
||||
@Schema(description = "Whether to allow duplicates or not", example = "true")
|
||||
private Boolean allowDuplicates;
|
||||
|
||||
@Schema(description = "Maximum bookmark level required", example = "2")
|
||||
private Integer bookmarkLevel;
|
||||
}
|
||||
@@ -15,6 +15,9 @@ public class AddPageNumbersRequest extends PDFWithPageNums {
|
||||
allowableValues = {"small", "medium", "large"})
|
||||
private String customMargin;
|
||||
|
||||
private float fontSize;
|
||||
private String fontType;
|
||||
|
||||
@Schema(description = "Position: 1 of 9 positions", minimum = "1", maximum = "9")
|
||||
private int position;
|
||||
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
package stirling.software.SPDF.repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import stirling.software.SPDF.model.User;
|
||||
|
||||
import java.util.Optional;
|
||||
import stirling.software.SPDF.model.User;
|
||||
|
||||
@Repository
|
||||
public interface UserRepository extends JpaRepository<User, Long> {
|
||||
Optional<User> findByUsernameIgnoreCase(String username);
|
||||
|
||||
@Query("FROM User u LEFT JOIN FETCH u.settings where upper(u.username) = upper(:username)")
|
||||
Optional<User> findByUsernameIgnoreCaseWithSettings(String username);
|
||||
Optional<User> findByUsernameIgnoreCaseWithSettings(@Param("username") String username);
|
||||
|
||||
|
||||
Optional<User> findByUsername(String username);
|
||||
|
||||
|
||||
@@ -262,5 +262,4 @@ public class GeneralUtils {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,10 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
addPageNumbers.fontSize=Font Size
|
||||
addPageNumbers.fontName=Font Name
|
||||
pdfPrompt=Изберете PDF(и)
|
||||
multiPdfPrompt=Изберете PDF (2+)
|
||||
multiPdfDropPrompt=Изберете (или плъзнете и пуснете) всички PDF файлове, от които се нуждаете
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
addPageNumbers.fontSize=Font Size
|
||||
addPageNumbers.fontName=Font Name
|
||||
pdfPrompt=Selecciona PDF(s)
|
||||
multiPdfPrompt=Selecciona PDFs (2+)
|
||||
multiPdfDropPrompt=Selecciona (o arrossega) els documents PDF
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
addPageNumbers.fontSize=Font Size
|
||||
addPageNumbers.fontName=Font Name
|
||||
pdfPrompt=Vyberte PDF soubory
|
||||
multiPdfPrompt=Vyberte PDF soubory (2+)
|
||||
multiPdfDropPrompt=Vyberte (nebo přetáhněte) všechny požadované PDF soubory
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,10 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
addPageNumbers.fontSize=Font Size
|
||||
addPageNumbers.fontName=Font Name
|
||||
pdfPrompt=PDF(s) auswählen
|
||||
multiPdfPrompt=PDFs auswählen(2+)
|
||||
multiPdfDropPrompt=Wählen Sie alle gewünschten PDFs aus (oder ziehen Sie sie per Drag & Drop hierhin)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
addPageNumbers.fontSize=Font Size
|
||||
addPageNumbers.fontName=Font Name
|
||||
pdfPrompt=Επιλογή PDF(s)
|
||||
multiPdfPrompt=Επιλογή PDFs (2+)
|
||||
multiPdfDropPrompt=Επιλογή (ή τράβηγμα αρχείου και απόθεση) όλων των PDF που χρειάζεστε
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
###########
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
addPageNumbers.fontSize=Font Size
|
||||
addPageNumbers.fontName=Font Name
|
||||
pdfPrompt=Select PDF(s)
|
||||
multiPdfPrompt=Select PDFs (2+)
|
||||
multiPdfDropPrompt=Select (or drag & drop) all PDFs you require
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
addPageNumbers.fontSize=Font Size
|
||||
addPageNumbers.fontName=Font Name
|
||||
pdfPrompt=Select PDF(s)
|
||||
multiPdfPrompt=Select PDFs (2+)
|
||||
multiPdfDropPrompt=Select (or drag & drop) all PDFs you require
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
addPageNumbers.fontSize=Font Size
|
||||
addPageNumbers.fontName=Font Name
|
||||
pdfPrompt=Seleccionar PDF(s)
|
||||
multiPdfPrompt=Seleccionar PDFs (2+)
|
||||
multiPdfDropPrompt=Seleccione (o arrastre y suelte) todos los PDFs que quiera
|
||||
@@ -179,7 +180,7 @@ adminUserSettings.user=Usuario
|
||||
adminUserSettings.addUser=Añadir Nuevo Usuario
|
||||
adminUserSettings.deleteUser=Eliminar Usuario
|
||||
adminUserSettings.confirmDeleteUser=¿Se debe eliminar al usuario?
|
||||
adminUserSettings.confirmChangeUserStatus= Se debe habilitar/deshabilitar el usuario?
|
||||
adminUserSettings.confirmChangeUserStatus=Se debe habilitar/deshabilitar el usuario?
|
||||
adminUserSettings.usernameInfo=El nombre de usuario solo puede contener letras, números y los siguientes caracteres especiales @._+- o debe ser una dirección de correo electrónico válida.
|
||||
adminUserSettings.roles=Roles
|
||||
adminUserSettings.role=Rol
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
addPageNumbers.fontSize=Font Size
|
||||
addPageNumbers.fontName=Font Name
|
||||
pdfPrompt=Hautatu PDFa(k)
|
||||
multiPdfPrompt=Hautatu PDFak (2+)
|
||||
multiPdfDropPrompt=Hautatu (edo arrastatu eta jaregin) nahi dituzun PDFak
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
addPageNumbers.fontSize=Font Size
|
||||
addPageNumbers.fontName=Font Name
|
||||
pdfPrompt=Sélectionnez le(s) PDF
|
||||
multiPdfPrompt=Sélectionnez les PDF
|
||||
multiPdfDropPrompt=Sélectionnez (ou glissez-déposez) tous les PDF dont vous avez besoin
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
addPageNumbers.fontSize=Font Size
|
||||
addPageNumbers.fontName=Font Name
|
||||
pdfPrompt=Roghnaigh PDF(s)
|
||||
multiPdfPrompt=Roghnaigh PDFs (2+)
|
||||
multiPdfDropPrompt=Roghnaigh (nó tarraing & scaoil) gach PDF atá uait
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
addPageNumbers.fontSize=Font Size
|
||||
addPageNumbers.fontName=Font Name
|
||||
pdfPrompt=पीडीएफ़(फ़ाइलें) चुनें
|
||||
multiPdfPrompt=पीडीएफ़(फ़ाइलें) चुनें (2+)
|
||||
multiPdfDropPrompt=सभी पीडीएफ़(फ़ाइलें) को चुनें (या खींचें और छोड़ें)
|
||||
@@ -109,25 +110,25 @@ pipelineOptions.validateButton=Validate
|
||||
#############
|
||||
# NAVBAR #
|
||||
#############
|
||||
navbar.favorite=Favorites
|
||||
navbar.favorite=पसंदीदा
|
||||
navbar.darkmode=डार्क मोड
|
||||
navbar.language=Languages
|
||||
navbar.language=भाषा
|
||||
navbar.settings=सेटिंग्स
|
||||
navbar.allTools=Tools
|
||||
navbar.multiTool=Multi Tools
|
||||
navbar.sections.organize=Organize
|
||||
navbar.sections.convertTo=Convert to PDF
|
||||
navbar.sections.convertFrom=Convert from PDF
|
||||
navbar.sections.security=Sign & Security
|
||||
navbar.sections.advance=Advanced
|
||||
navbar.sections.edit=View & Edit
|
||||
navbar.allTools=साधन
|
||||
navbar.multiTool=विभिन्न साधन
|
||||
navbar.sections.organize=संगठित करें
|
||||
navbar.sections.convertTo=पीडीएफ में कनवर्ट करें
|
||||
navbar.sections.convertFrom=पीडीएफ से कनवर्ट करें
|
||||
navbar.sections.security=संकेत और सुरक्षा
|
||||
navbar.sections.advance=उन्नत
|
||||
navbar.sections.edit=देखें और संपादित करें
|
||||
|
||||
#############
|
||||
# SETTINGS #
|
||||
#############
|
||||
settings.title=सेटिंग्स
|
||||
settings.update=अपडेट उपलब्ध है
|
||||
settings.updateAvailable={0} is the current installed version. A new version ({1}) is available.
|
||||
settings.updateAvailable={0} वर्तमान स्थापित संस्करण है. एक नया संस्करण ({1}) उपलब्ध है.
|
||||
settings.appVersion=ऐप संस्करण:
|
||||
settings.downloadOption.title=डाउनलोड विकल्प चुनें (एकल फ़ाइल गैर-ज़िप डाउनलोड के लिए):
|
||||
settings.downloadOption.1=एक ही विंडो में खोलें
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
addPageNumbers.fontSize=Font Size
|
||||
addPageNumbers.fontName=Font Name
|
||||
pdfPrompt=Odaberi PDF(ove)
|
||||
multiPdfPrompt=Odaberi PDF-ove (2+)
|
||||
multiPdfDropPrompt=Odaberi (ili povuci i ispusti) sve potrebne PDF-ove
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
addPageNumbers.fontSize=Font Size
|
||||
addPageNumbers.fontName=Font Name
|
||||
pdfPrompt=Válasszon PDF-fájlokat
|
||||
multiPdfPrompt=Válasszon PDF-fájlokat (2+)
|
||||
multiPdfDropPrompt=Válassza ki (vagy húzza ide) az összes szükséges PDF-fájlt
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl=right to left)
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
addPageNumbers.fontSize=Font Size
|
||||
addPageNumbers.fontName=Font Name
|
||||
pdfPrompt=Pilih PDF
|
||||
multiPdfPrompt=Pilih PDF (2+)
|
||||
multiPdfDropPrompt=Pilih (atau seret & letakkan)) semua PDF yang Anda butuhkan
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
addPageNumbers.fontSize=Dimensione del font
|
||||
addPageNumbers.fontName=Nome del font
|
||||
pdfPrompt=Scegli PDF
|
||||
multiPdfPrompt=Scegli 2 o più PDF
|
||||
multiPdfDropPrompt=Scegli (o trascina e rilascia) uno o più PDF
|
||||
@@ -802,7 +803,7 @@ ocr.submit=Scansiona testo nel PDF con OCR
|
||||
extractImages.title=Estrai immagini
|
||||
extractImages.header=Estrai immagini
|
||||
extractImages.selectText=Seleziona il formato in cui salvare le immagini estratte
|
||||
extractImages.allowDuplicates=Save duplicate images
|
||||
extractImages.allowDuplicates=Salva le immagini duplicate
|
||||
extractImages.submit=Estrai
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
addPageNumbers.fontSize=Font Size
|
||||
addPageNumbers.fontName=Font Name
|
||||
pdfPrompt=PDFを選択
|
||||
multiPdfPrompt=PDFを選択 (2つ以上)
|
||||
multiPdfDropPrompt=PDFを選択 (又はドラッグ&ドロップ)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
addPageNumbers.fontSize=Font Size
|
||||
addPageNumbers.fontName=Font Name
|
||||
pdfPrompt=PDF 파일 선택
|
||||
multiPdfPrompt=여러 PDF 파일 선택
|
||||
multiPdfDropPrompt=사용할 모든 PDF 문서를 선택(또는 드래그 앤 드롭)합니다
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
addPageNumbers.fontSize=Font Size
|
||||
addPageNumbers.fontName=Font Name
|
||||
pdfPrompt=Selecteer PDF('s)
|
||||
multiPdfPrompt=Selecteer PDF's (2+)
|
||||
multiPdfDropPrompt=Selecteer (of sleep & zet neer) alle PDF's die je nodig hebt
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
addPageNumbers.fontSize=Font Size
|
||||
addPageNumbers.fontName=Font Name
|
||||
pdfPrompt=Velg PDF(er)
|
||||
multiPdfPrompt=Velg PDF-filer (2+)
|
||||
multiPdfDropPrompt=Velg (eller dra og slipp) alle PDF-ene du trenger
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
addPageNumbers.fontSize=Font Size
|
||||
addPageNumbers.fontName=Font Name
|
||||
pdfPrompt=Wybierz PDF
|
||||
multiPdfPrompt=Wybierz PDF (2+)
|
||||
multiPdfDropPrompt=Wybierz (lub przeciągnij i puść) wszystkie dokumenty PDF
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
addPageNumbers.fontSize=Font Size
|
||||
addPageNumbers.fontName=Font Name
|
||||
pdfPrompt=Selecione PDF(s)
|
||||
multiPdfPrompt=Selecione PDFs (2+)
|
||||
multiPdfDropPrompt=Selecione (ou arraste e solte) todos os PDFs necessários
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
addPageNumbers.fontSize=Font Size
|
||||
addPageNumbers.fontName=Font Name
|
||||
pdfPrompt=Selecione PDF(s)
|
||||
multiPdfPrompt=Selecione PDFs (2+)
|
||||
multiPdfDropPrompt=Selecione (ou arraste e solte) todos os PDFs necessários
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,10 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
addPageNumbers.fontSize=Font Size
|
||||
addPageNumbers.fontName=Font Name
|
||||
pdfPrompt=Выберите PDF(ы)
|
||||
multiPdfPrompt=Выберите PDFы (2+)
|
||||
multiPdfDropPrompt=Выберите (или перетащите) все необходимые PDFы
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
addPageNumbers.fontSize=Font Size
|
||||
addPageNumbers.fontName=Font Name
|
||||
pdfPrompt=Vyberte PDF súbor(y)
|
||||
multiPdfPrompt=Vyberte PDF súbory (2+)
|
||||
multiPdfDropPrompt=Vyberte (alebo pretiahnite) všetky požadované PDF súbory
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
addPageNumbers.fontSize=Font Size
|
||||
addPageNumbers.fontName=Font Name
|
||||
pdfPrompt=Odaberi PDF(ove)
|
||||
multiPdfPrompt=Odaberi PDF-ove (2+)
|
||||
multiPdfDropPrompt=Odaberi (prevuci i pusti ) sve PDF-ove koji su vam potrebni
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,10 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
addPageNumbers.fontSize=Font Size
|
||||
addPageNumbers.fontName=Font Name
|
||||
pdfPrompt=เลือก PDF
|
||||
multiPdfPrompt=เลือก PDF หลายไฟล์ (2 ขึ้นไป)
|
||||
multiPdfDropPrompt=เลือก (หรือลากและวาง) PDF ทั้งหมดที่คุณต้องการ
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
addPageNumbers.fontSize=Font Size
|
||||
addPageNumbers.fontName=Font Name
|
||||
pdfPrompt=PDF(leri) seçin
|
||||
multiPdfPrompt=PDFleri seçin (2+)
|
||||
multiPdfDropPrompt=Tüm gerekli PDF'leri seçin (ya da sürükleyip bırakın)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
addPageNumbers.fontSize=Font Size
|
||||
addPageNumbers.fontName=Font Name
|
||||
pdfPrompt=Оберіть PDF(и)
|
||||
multiPdfPrompt=Оберіть PDFи (2+)
|
||||
multiPdfDropPrompt=Оберіть (або перетягніть) всі необхідні PDFи
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
addPageNumbers.fontSize=Font Size
|
||||
addPageNumbers.fontName=Font Name
|
||||
pdfPrompt=Chọn (các) tệp PDF
|
||||
multiPdfPrompt=Chọn các tệp PDF (2+)
|
||||
multiPdfDropPrompt=Chọn (hoặc kéo và thả) tất cả các tệp PDF bạn cần
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
addPageNumbers.fontSize=Font Size
|
||||
addPageNumbers.fontName=Font Name
|
||||
pdfPrompt=选择PDF
|
||||
multiPdfPrompt=选择多个PDF(2个或更多)
|
||||
multiPdfDropPrompt=选择(或拖拽)所需的PDF
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
###########
|
||||
# Generic #
|
||||
###########
|
||||
# the direction that the language is written (ltr=left to right, rtl = right to left)
|
||||
# the direction that the language is written (ltr = left to right, rtl = right to left)
|
||||
language.direction=ltr
|
||||
|
||||
addPageNumbers.fontSize=Font Size
|
||||
addPageNumbers.fontName=Font Name
|
||||
pdfPrompt=選擇 PDF 檔案
|
||||
multiPdfPrompt=選擇多個 PDF 檔案
|
||||
multiPdfDropPrompt=選擇(或拖放)所有需要的 PDF 檔案
|
||||
|
||||
@@ -18,7 +18,17 @@
|
||||
text-align: center; /* Centers the text inside the div */
|
||||
width: 100%; /* Full width to center the text properly */
|
||||
}
|
||||
.stirling-link {
|
||||
text-decoration: none; /* Remove the underline */
|
||||
color: inherit; /* Keep the text color the same as the surrounding text */
|
||||
cursor: pointer; /* Change the cursor to indicate it's clickable */
|
||||
font-weight: bold; /* Make it bold to subtly hint that it's clickable */
|
||||
transition: color 0.3s ease; /* Add a smooth transition effect for color change on hover */
|
||||
}
|
||||
|
||||
.stirling-link:hover {
|
||||
color: #007BFF; /* Change the color on hover to a noticeable link color */
|
||||
}
|
||||
.footer-icon {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
@@ -70,12 +70,12 @@ class PdfActionsManager {
|
||||
|
||||
insertFileButtonCallback(e) {
|
||||
var imgContainer = this.getPageContainer(e.target);
|
||||
this.addPdfs(imgContainer);
|
||||
this.addFiles(imgContainer);
|
||||
}
|
||||
|
||||
setActions({ movePageTo, addPdfs, rotateElement }) {
|
||||
setActions({ movePageTo, addFiles, rotateElement }) {
|
||||
this.movePageTo = movePageTo;
|
||||
this.addPdfs = addPdfs;
|
||||
this.addFiles = addFiles;
|
||||
this.rotateElement = rotateElement;
|
||||
|
||||
this.moveUpButtonCallback = this.moveUpButtonCallback.bind(this);
|
||||
@@ -153,7 +153,7 @@ class PdfActionsManager {
|
||||
const insertFileButtonRight = document.createElement("button");
|
||||
insertFileButtonRight.classList.add("btn", "btn-primary", "pdf-actions_insert-file-button");
|
||||
insertFileButtonRight.innerHTML = `<span class="material-symbols-rounded">add</span>`;
|
||||
insertFileButtonRight.onclick = () => addPdfs();
|
||||
insertFileButtonRight.onclick = () => addFiles();
|
||||
insertFileButtonRightContainer.appendChild(insertFileButtonRight);
|
||||
|
||||
div.appendChild(insertFileButtonRightContainer);
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
|
||||
<!-- powered by section -->
|
||||
<div class="footer-powered-by">
|
||||
<span th:text="#{poweredBy} + ' Stirling PDF'"></span>
|
||||
</div>
|
||||
<span th:text="#{poweredBy}"></span>
|
||||
<a href="https://stirlingpdf.com" class="stirling-link">Stirling PDF</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
@@ -397,11 +397,6 @@
|
||||
<a href="https://discord.gg/Cn8pWhQRxZ" class="mx-1" role="button" th:title="#{joinDiscord}">
|
||||
<img th:src="@{'/images/discord.svg'}" alt="discord">
|
||||
</a>
|
||||
<a href="https://github.com/sponsors/Frooodle" class="mx-1" role="button" th:title="#{donate}">
|
||||
<span class="material-symbols-rounded fill footer-icon" style="font-size: 2.5rem;">
|
||||
favorite
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<a th:href="@{'/swagger-ui/index.html'}" class="btn btn-sm btn-outline-primary mx-1" role="button"
|
||||
|
||||
@@ -1,140 +1,153 @@
|
||||
<!DOCTYPE html>
|
||||
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
|
||||
<head>
|
||||
<head>
|
||||
<th:block th:insert="~{fragments/common :: head(title=#{addPageNumbers.title}, header=#{addPageNumbers.header})}"></th:block>
|
||||
<style>
|
||||
.a4container {
|
||||
position: relative;
|
||||
width: 50%;
|
||||
aspect-ratio: 0.707/1;
|
||||
border: 1px solid #ddd;
|
||||
box-sizing: border-box;
|
||||
background-color: white;
|
||||
}
|
||||
<style>
|
||||
.a4container {
|
||||
position: relative;
|
||||
width: 50%;
|
||||
aspect-ratio: 0.707/1;
|
||||
border: 1px solid #ddd;
|
||||
box-sizing: border-box;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.pageNumber {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 1em;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
background-color: #ccc;
|
||||
width: 15%;
|
||||
height: 15%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
.pageNumber {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 1em;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
background-color: #ccc;
|
||||
width: 15%;
|
||||
height: 15%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.pageNumber:hover {
|
||||
background-color: #eee;
|
||||
}
|
||||
.pageNumber:hover {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
#myForm {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
#myForm {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.selectedPosition {
|
||||
background-color: #0a0;
|
||||
}
|
||||
.selectedPosition {
|
||||
background-color: #0a0;
|
||||
}
|
||||
|
||||
.selectedPosition.selectedHovered {
|
||||
background-color: #006600;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
.selectedPosition.selectedHovered {
|
||||
background-color: #006600;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<th:block th:insert="~{fragments/common :: game}"></th:block>
|
||||
<div id="page-container">
|
||||
<div id="content-wrap">
|
||||
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
|
||||
<br><br>
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6 bg-card">
|
||||
<div class="tool-header">
|
||||
<span class="material-symbols-rounded tool-header-icon other">123</span>
|
||||
<span class="tool-header-text" th:text="#{addPageNumbers.header}"></span>
|
||||
</div>
|
||||
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/misc/add-page-numbers'}">
|
||||
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
|
||||
<br>
|
||||
<div class="mb-3">
|
||||
<label for="customMargin" th:text="#{addPageNumbers.selectText.2}"></label>
|
||||
<select class="form-control" id="customMargin" name="customMargin">
|
||||
<option value="small" th:text="#{sizes.small}"></option>
|
||||
<option value="medium" selected th:text="#{sizes.medium}"></option>
|
||||
<option value="large" th:text="#{sizes.large}"></option>
|
||||
<option value="x-large" th:text="#{sizes.x-large}"></option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label th:text="#{addPageNumbers.selectText.3}"></label>
|
||||
<div class="a4container">
|
||||
<div class="pageNumber" id="1" style="top: 10%; left: 10%;">1</div>
|
||||
<div class="pageNumber" id="2" style="top: 10%; left: 50%;">2</div>
|
||||
<div class="pageNumber" id="3" style="top: 10%; left: 90%;">3</div>
|
||||
<div class="pageNumber" id="4" style="top: 50%; left: 10%;">4</div>
|
||||
<div class="pageNumber" id="5" style="top: 50%; left: 50%;">5</div>
|
||||
<div class="pageNumber" id="6" style="top: 50%; left: 90%;">6</div>
|
||||
<div class="pageNumber" id="7" style="top: 90%; left: 10%;">7</div>
|
||||
<div class="pageNumber selectedPosition" id="8" style="top: 90%; left: 50%;">8</div>
|
||||
<div class="pageNumber" id="9" style="top: 90%; left: 90%;">9</div>
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" id="numberInput" name="position" value="8">
|
||||
<div class="mb-3">
|
||||
<label for="startingNumber" th:text="#{addPageNumbers.selectText.4}"></label>
|
||||
<input type="number" class="form-control" id="startingNumber" name="startingNumber" min="1" required value="1">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="pagesToNumber" th:text="#{addPageNumbers.selectText.5}"></label>
|
||||
<input type="text" class="form-control" id="pagesToNumber" name="pagesToNumber" th:placeholder="#{addPageNumbers.numberPagesDesc}">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="customText" th:text="#{addPageNumbers.selectText.6}"></label>
|
||||
<input type="text" class="form-control" id="customText" name="customText" th:placeholder="#{addPageNumbers.customNumberDesc}">
|
||||
</div>
|
||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{addPageNumbers.submit}"></button>
|
||||
</form>
|
||||
</div>
|
||||
<body>
|
||||
<th:block th:insert="~{fragments/common :: game}"></th:block>
|
||||
<div id="page-container">
|
||||
<div id="content-wrap">
|
||||
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
|
||||
<br><br>
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6 bg-card">
|
||||
<div class="tool-header">
|
||||
<span class="material-symbols-rounded tool-header-icon other">123</span>
|
||||
<span class="tool-header-text" th:text="#{addPageNumbers.header}"></span>
|
||||
</div>
|
||||
<form method="post" enctype="multipart/form-data" th:action="@{'/api/v1/misc/add-page-numbers'}">
|
||||
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
|
||||
<br>
|
||||
<div class="mb-3">
|
||||
<label for="customMargin" th:text="#{addPageNumbers.selectText.2}"></label>
|
||||
<select class="form-control" id="customMargin" name="customMargin">
|
||||
<option value="small" th:text="#{sizes.small}"></option>
|
||||
<option value="medium" selected th:text="#{sizes.medium}"></option>
|
||||
<option value="large" th:text="#{sizes.large}"></option>
|
||||
<option value="x-large" th:text="#{sizes.x-large}"></option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label th:text="#{addPageNumbers.selectText.3}"></label>
|
||||
<div class="a4container">
|
||||
<div class="pageNumber" id="1" style="top: 10%; left: 10%;">1</div>
|
||||
<div class="pageNumber" id="2" style="top: 10%; left: 50%;">2</div>
|
||||
<div class="pageNumber" id="3" style="top: 10%; left: 90%;">3</div>
|
||||
<div class="pageNumber" id="4" style="top: 50%; left: 10%;">4</div>
|
||||
<div class="pageNumber" id="5" style="top: 50%; left: 50%;">5</div>
|
||||
<div class="pageNumber" id="6" style="top: 50%; left: 90%;">6</div>
|
||||
<div class="pageNumber" id="7" style="top: 90%; left: 10%;">7</div>
|
||||
<div class="pageNumber selectedPosition" id="8" style="top: 90%; left: 50%;">8</div>
|
||||
<div class="pageNumber" id="9" style="top: 90%; left: 90%;">9</div>
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" id="numberInput" name="position" value="8">
|
||||
<div class="mb-3">
|
||||
<label for="fontSize" th:text="#{addPageNumbers.fontSize}"></label>
|
||||
<input type="number" class="form-control" id="fontSize" name="fontSize" min="1" value="12" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="fontType" th:text="#{addPageNumbers.fontName}"></label>
|
||||
<select class="form-control" id="fontType" name="fontType">
|
||||
<option value="Times Roman">Times</option>
|
||||
|
||||
<option value="Helvetica">Helvetica</option>
|
||||
<option value="Courier New">Courier</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="startingNumber" th:text="#{addPageNumbers.selectText.4}"></label>
|
||||
<input type="number" class="form-control" id="startingNumber" name="startingNumber" min="1" required value="1">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="pagesToNumber" th:text="#{addPageNumbers.selectText.5}"></label>
|
||||
<input type="text" class="form-control" id="pagesToNumber" name="pagesToNumber" th:placeholder="#{addPageNumbers.numberPagesDesc}">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="customText" th:text="#{addPageNumbers.selectText.6}"></label>
|
||||
<input type="text" class="form-control" id="customText" name="customText" th:placeholder="#{addPageNumbers.customNumberDesc}">
|
||||
</div>
|
||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{addPageNumbers.submit}"></button>
|
||||
</form>
|
||||
</div>
|
||||
<script>
|
||||
let cells = document.querySelectorAll('.pageNumber');
|
||||
let inputField = document.getElementById('numberInput');
|
||||
|
||||
cells.forEach(cell => {
|
||||
cell.addEventListener('click', function(e) {
|
||||
cells.forEach(cell => {
|
||||
cell.classList.remove('selectedPosition'); // Remove selected class from all cells
|
||||
cell.classList.remove('selectedHovered'); // Also remove selectedHovered class
|
||||
});
|
||||
let selectedLocation = e.target.id;
|
||||
inputField.value = selectedLocation;
|
||||
e.target.classList.add('selectedPosition'); // Add selected class to clicked cell
|
||||
e.target.classList.add('selectedHovered'); // Add selectedHovered class
|
||||
});
|
||||
|
||||
cell.addEventListener('mouseenter', function(e) {
|
||||
if(e.target.classList.contains('selectedPosition')) {
|
||||
e.target.classList.add('selectedHovered');
|
||||
}
|
||||
});
|
||||
|
||||
cell.addEventListener('mouseleave', function(e) {
|
||||
if(e.target.classList.contains('selectedPosition')) {
|
||||
e.target.classList.remove('selectedHovered');
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
<script>
|
||||
let cells = document.querySelectorAll('.pageNumber');
|
||||
let inputField = document.getElementById('numberInput');
|
||||
|
||||
cells.forEach(cell => {
|
||||
cell.addEventListener('click', function(e) {
|
||||
cells.forEach(cell => {
|
||||
cell.classList.remove('selectedPosition'); // Remove selected class from all cells
|
||||
cell.classList.remove('selectedHovered'); // Also remove selectedHovered class
|
||||
});
|
||||
let selectedLocation = e.target.id;
|
||||
inputField.value = selectedLocation;
|
||||
e.target.classList.add('selectedPosition'); // Add selected class to clicked cell
|
||||
e.target.classList.add('selectedHovered'); // Add selectedHovered class
|
||||
});
|
||||
|
||||
cell.addEventListener('mouseenter', function(e) {
|
||||
if(e.target.classList.contains('selectedPosition')) {
|
||||
e.target.classList.add('selectedHovered');
|
||||
}
|
||||
});
|
||||
|
||||
cell.addEventListener('mouseleave', function(e) {
|
||||
if(e.target.classList.contains('selectedPosition')) {
|
||||
e.target.classList.remove('selectedHovered');
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user