print (WIP), fake scan (WIP) and text conversion for ultra-lite (#1098)
* Changes! * lang * fake scan init, print init and pdf to text for exe * Hardening suggestions for Stirling-PDF / changes (#1099) * Switch order of literals to prevent NullPointerException * Introduced protections against predictable RNG abuse --------- Co-authored-by: pixeebot[bot] <104101892+pixeebot[bot]@users.noreply.github.com> * Update README.md * install custom fonts * Formats etc * version bump * disable WIP work * remove chinese font --------- Co-authored-by: pixeebot[bot] <104101892+pixeebot[bot]@users.noreply.github.com> Co-authored-by: systo <systo@host.docker.internal>pull/1108/head
parent
6c052a7b25
commit
71e93e3cb5
19 changed files with 491 additions and 140 deletions
@ -0,0 +1,67 @@ |
||||
#!/bin/bash |
||||
|
||||
LANGS=$1 |
||||
|
||||
# Function to install a font package |
||||
install_font() { |
||||
echo "Installing font package: $1" |
||||
if ! apk add "$1" --no-cache; then |
||||
echo "Failed to install $1" |
||||
fi |
||||
} |
||||
|
||||
# Install common fonts used across many languages |
||||
#common_fonts=( |
||||
# font-terminus |
||||
# font-dejavu |
||||
# font-noto |
||||
# font-noto-cjk |
||||
# font-awesome |
||||
# font-noto-extra |
||||
#) |
||||
# |
||||
#for font in "${common_fonts[@]}"; do |
||||
# install_font $font |
||||
#done |
||||
|
||||
# Map languages to specific font packages |
||||
declare -A language_fonts=( |
||||
["ar_AR"]="font-noto-arabic" |
||||
["zh_CN"]="font-isas-misc" |
||||
["zh_TW"]="font-isas-misc" |
||||
["ja_JP"]="font-noto font-noto-thai font-noto-tibetan font-ipa font-sony-misc font-jis-misc" |
||||
["ru_RU"]="font-vollkorn font-misc-cyrillic font-mutt-misc font-screen-cyrillic font-winitzki-cyrillic font-cronyx-cyrillic" |
||||
["sr_LATN_RS"]="font-vollkorn font-misc-cyrillic font-mutt-misc font-screen-cyrillic font-winitzki-cyrillic font-cronyx-cyrillic" |
||||
["uk_UA"]="font-vollkorn font-misc-cyrillic font-mutt-misc font-screen-cyrillic font-winitzki-cyrillic font-cronyx-cyrillic" |
||||
["ko_KR"]="font-noto font-noto-thai font-noto-tibetan" |
||||
["el_GR"]="font-noto" |
||||
["hi_IN"]="font-noto-devanagari" |
||||
["bg_BG"]="font-vollkorn font-misc-cyrillic" |
||||
["GENERAL"]="font-terminus font-dejavu font-noto font-noto-cjk font-awesome font-noto-extra" |
||||
) |
||||
|
||||
# Install fonts for other languages which generally do not need special packages beyond 'font-noto' |
||||
other_langs=("en_GB" "en_US" "de_DE" "fr_FR" "es_ES" "ca_CA" "it_IT" "pt_BR" "nl_NL" "sv_SE" "pl_PL" "ro_RO" "hu_HU" "tr_TR" "id_ID" "eu_ES") |
||||
if [[ $LANGS == "ALL" ]]; then |
||||
# Install all fonts from the language_fonts map |
||||
for fonts in "${language_fonts[@]}"; do |
||||
for font in $fonts; do |
||||
install_font $font |
||||
done |
||||
done |
||||
else |
||||
# Split comma-separated languages and install necessary fonts |
||||
IFS=',' read -ra LANG_CODES <<< "$LANGS" |
||||
for code in "${LANG_CODES[@]}"; do |
||||
if [[ " ${other_langs[@]} " =~ " ${code} " ]]; then |
||||
install_font font-noto |
||||
else |
||||
fonts_to_install=${language_fonts[$code]} |
||||
if [ ! -z "$fonts_to_install" ]; then |
||||
for font in $fonts_to_install; do |
||||
install_font $font |
||||
done |
||||
fi |
||||
fi |
||||
done |
||||
fi |
@ -0,0 +1,106 @@ |
||||
package stirling.software.SPDF.controller.api.misc; |
||||
|
||||
import java.awt.Graphics; |
||||
import java.awt.Graphics2D; |
||||
import java.awt.image.BufferedImage; |
||||
import java.awt.print.PageFormat; |
||||
import java.awt.print.Printable; |
||||
import java.awt.print.PrinterException; |
||||
import java.awt.print.PrinterJob; |
||||
import java.io.IOException; |
||||
import java.util.Arrays; |
||||
|
||||
import javax.imageio.ImageIO; |
||||
import javax.print.PrintService; |
||||
import javax.print.PrintServiceLookup; |
||||
|
||||
import org.apache.pdfbox.Loader; |
||||
import org.apache.pdfbox.pdmodel.PDDocument; |
||||
import org.apache.pdfbox.printing.PDFPageable; |
||||
import org.springframework.http.HttpStatus; |
||||
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.swagger.v3.oas.annotations.Operation; |
||||
import io.swagger.v3.oas.annotations.tags.Tag; |
||||
|
||||
import stirling.software.SPDF.model.api.misc.PrintFileRequest; |
||||
|
||||
@RestController |
||||
@RequestMapping("/api/v1/misc") |
||||
@Tag(name = "Misc", description = "Miscellaneous APIs") |
||||
public class PrintFileController { |
||||
|
||||
//TODO
|
||||
//@PostMapping(value = "/print-file", consumes = "multipart/form-data")
|
||||
//@Operation(
|
||||
// summary = "Prints PDF/Image file to a set printer",
|
||||
// description =
|
||||
// "Input of PDF or Image along with a printer name/URL/IP to match against to send it to (Fire and forget) Input:Any Output:N/A Type:SISO")
|
||||
public ResponseEntity<String> printFile(@ModelAttribute PrintFileRequest request) |
||||
throws IOException { |
||||
MultipartFile file = request.getFileInput(); |
||||
String printerName = request.getPrinterName(); |
||||
String contentType = file.getContentType(); |
||||
try { |
||||
// Find matching printer
|
||||
PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null); |
||||
PrintService selectedService = |
||||
Arrays.stream(services) |
||||
.filter( |
||||
service -> |
||||
service.getName().toLowerCase().contains(printerName)) |
||||
.findFirst() |
||||
.orElseThrow( |
||||
() -> |
||||
new IllegalArgumentException( |
||||
"No matching printer found")); |
||||
|
||||
System.out.println("Selected Printer: " + selectedService.getName()); |
||||
|
||||
if ("application/pdf".equals(contentType)) { |
||||
PDDocument document = Loader.loadPDF(file.getBytes()); |
||||
PrinterJob job = PrinterJob.getPrinterJob(); |
||||
job.setPrintService(selectedService); |
||||
job.setPageable(new PDFPageable(document)); |
||||
job.print(); |
||||
document.close(); |
||||
} else if (contentType.startsWith("image/")) { |
||||
BufferedImage image = ImageIO.read(file.getInputStream()); |
||||
PrinterJob job = PrinterJob.getPrinterJob(); |
||||
job.setPrintService(selectedService); |
||||
job.setPrintable( |
||||
new Printable() { |
||||
public int print( |
||||
Graphics graphics, PageFormat pageFormat, int pageIndex) |
||||
throws PrinterException { |
||||
if (pageIndex != 0) { |
||||
return NO_SUCH_PAGE; |
||||
} |
||||
Graphics2D g2d = (Graphics2D) graphics; |
||||
g2d.translate( |
||||
pageFormat.getImageableX(), pageFormat.getImageableY()); |
||||
g2d.drawImage( |
||||
image, |
||||
0, |
||||
0, |
||||
(int) pageFormat.getImageableWidth(), |
||||
(int) pageFormat.getImageableHeight(), |
||||
null); |
||||
return PAGE_EXISTS; |
||||
} |
||||
}); |
||||
job.print(); |
||||
} |
||||
return new ResponseEntity<>( |
||||
"File printed successfully to " + selectedService.getName(), HttpStatus.OK); |
||||
} catch (Exception e) { |
||||
System.err.println("Failed to print: " + e.getMessage()); |
||||
return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,15 @@ |
||||
package stirling.software.SPDF.model.api.misc; |
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
||||
import lombok.Data; |
||||
import lombok.EqualsAndHashCode; |
||||
import stirling.software.SPDF.model.api.PDFFile; |
||||
|
||||
@Data |
||||
@EqualsAndHashCode(callSuper = true) |
||||
public class PrintFileRequest extends PDFFile { |
||||
|
||||
@Schema(description = "Name of printer to match against", required = true) |
||||
private String printerName; |
||||
} |
@ -0,0 +1,29 @@ |
||||
<!DOCTYPE html> |
||||
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="http://www.thymeleaf.org"> |
||||
<head> |
||||
<th:block th:insert="~{fragments/common :: head(title=#{fakeScan.title}, header=#{fakeScan.header})}"></th:block> |
||||
</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"> |
||||
<h2 th:text="#{fakeScan.header}"></h2> |
||||
<form method="post" enctype="multipart/form-data" th:action="@{api/v1/misc/fake-scan}"> |
||||
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}"></div> |
||||
<br> |
||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{fakeScan.submit}"></button> |
||||
</form> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block> |
||||
</div> |
||||
</body> |
||||
</html> |
@ -0,0 +1,35 @@ |
||||
<!DOCTYPE html> |
||||
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="http://www.thymeleaf.org"> |
||||
<head> |
||||
<th:block th:insert="~{fragments/common :: head(title=#{printFile.title}, header=#{printFile.header})}"></th:block> |
||||
</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"> |
||||
<h2 th:text="#{printFile.header}"></h2> |
||||
<form action="#" th:action="@{api/v1/misc/print-file}" method="post" enctype="multipart/form-data"> |
||||
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf,image/*')}"></div> |
||||
<div class="card mb-3"> |
||||
<div class="card-body"> |
||||
<h4 th:text="#{printFile.selectText.1}">Select Printer</h4> <!-- Assuming the message code printFile.selectText.3 corresponds to "Select Printer" --> |
||||
<label for="printerName" th:text="#{printFile.selectText.2}">Printer Name:</label> <!-- Assuming the message code printFile.selectText.4 corresponds to "Printer Name:" --> |
||||
<input type="text" name="printerName" id="printerName" class="form-control"> |
||||
</div> |
||||
</div> |
||||
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{printFile.submit}"></button> |
||||
</form> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block> |
||||
</div> |
||||
</body> |
||||
</html> |
Loading…
Reference in new issue