sign changes min size, lite docker, merge fix #172
This commit is contained in:
parent
ecba6461df
commit
04032c0dfe
11 changed files with 124 additions and 85 deletions
71
.github/workflows/push-docker.yml
vendored
71
.github/workflows/push-docker.yml
vendored
|
@ -8,8 +8,14 @@ on:
|
||||||
- main
|
- main
|
||||||
jobs:
|
jobs:
|
||||||
push:
|
push:
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
config: [
|
||||||
|
{ dockerfile: "./Dockerfile", tagSuffix: "" },
|
||||||
|
{ dockerfile: "./Dockerfile-ultra-lite", tagSuffix: "-ultra-lite" },
|
||||||
|
{ dockerfile: "./Dockerfile-lite", tagSuffix: "-lite" }
|
||||||
|
]
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- uses: actions/checkout@v3.5.2
|
- uses: actions/checkout@v3.5.2
|
||||||
|
@ -46,17 +52,10 @@ jobs:
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ github.token }}
|
password: ${{ github.token }}
|
||||||
|
|
||||||
- name: Generate tags
|
- name: Convert repository owner to lowercase
|
||||||
id: meta
|
id: repoowner
|
||||||
uses: docker/metadata-action@v4.4.0
|
run: echo "::set-output name=lowercase::$(echo ${{ github.repository_owner }} | awk '{print tolower($0)}')"
|
||||||
with:
|
|
||||||
images: |
|
|
||||||
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
|
|
||||||
ghcr.io/${{ github.repository_owner }}/s-pdf
|
|
||||||
tags: |
|
|
||||||
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }},enable=${{ github.ref == 'refs/heads/master' }}
|
|
||||||
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/master' }}
|
|
||||||
type=raw,value=alpha,enable=${{ github.ref == 'refs/heads/main' }}
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2.1.0
|
uses: docker/setup-qemu-action@v2.1.0
|
||||||
|
@ -64,44 +63,28 @@ jobs:
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2.5.0
|
uses: docker/setup-buildx-action@v2.5.0
|
||||||
|
|
||||||
- name: Build and push main Dockerfile
|
|
||||||
|
- name: Generate tags
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v4.4.0
|
||||||
|
with:
|
||||||
|
images: |
|
||||||
|
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
|
||||||
|
ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf
|
||||||
|
tags: |
|
||||||
|
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}${{ matrix.config.tagSuffix }},enable=${{ github.ref == 'refs/heads/master' }}
|
||||||
|
type=raw,value=latest${{ matrix.config.tagSuffix }},enable=${{ github.ref == 'refs/heads/master' }}
|
||||||
|
type=raw,value=alpha,enable=${{ github.ref == 'refs/heads/main' }}
|
||||||
|
|
||||||
|
- name: Build and push Dockerfile
|
||||||
uses: docker/build-push-action@v4.0.0
|
uses: docker/build-push-action@v4.0.0
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: ./Dockerfile
|
dockerfile: ${{ matrix.config.dockerfile }}
|
||||||
push: true
|
push: true
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
platforms: linux/amd64,linux/arm64/v8
|
platforms: linux/amd64,linux/arm64/v8
|
||||||
|
|
||||||
- name: Generate tags
|
|
||||||
id: meta2
|
|
||||||
uses: docker/metadata-action@v4.4.0
|
|
||||||
with:
|
|
||||||
images: |
|
|
||||||
${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf
|
|
||||||
ghcr.io/${{ github.repository_owner }}/s-pdf
|
|
||||||
tags: |
|
|
||||||
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-ultra-light,enable=${{ github.ref == 'refs/heads/master' }}
|
|
||||||
type=raw,value=latest-ultra-light,enable=${{ github.ref == 'refs/heads/master' }}
|
|
||||||
type=raw,value=alpha-ultra-light,enable=${{ github.ref == 'refs/heads/main' }}
|
|
||||||
|
|
||||||
- name: Convert repository owner to lowercase
|
|
||||||
id: repoowner
|
|
||||||
run: echo "::set-output name=lowercase::$(echo ${{ github.repository_owner }} | awk '{print tolower($0)}')"
|
|
||||||
|
|
||||||
- name: Build and push Dockerfile-ultralite
|
|
||||||
uses: docker/build-push-action@v4.0.0
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: ./Dockerfile-ultralite
|
|
||||||
push: true
|
|
||||||
cache-from: type=gha
|
|
||||||
cache-to: type=gha,mode=max
|
|
||||||
tags: ${{ steps.meta2.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta2.outputs.labels }}
|
|
||||||
platforms: linux/amd64,linux/arm64/v8
|
|
||||||
|
|
||||||
|
|
23
Dockerfile-lite
Normal file
23
Dockerfile-lite
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# Build jbig2enc in a separate stage
|
||||||
|
FROM bellsoft/liberica-openjdk-debian:17
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get install -y --no-install-recommends \
|
||||||
|
libreoffice-core-nogui \
|
||||||
|
libreoffice-common \
|
||||||
|
libreoffice-writer-nogui \
|
||||||
|
libreoffice-calc-nogui \
|
||||||
|
libreoffice-impress-nogui \
|
||||||
|
unoconv && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Copy the application JAR file
|
||||||
|
COPY build/libs/*.jar app.jar
|
||||||
|
|
||||||
|
# Expose the application port
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
# Set environment variables
|
||||||
|
ENV GROUPS_TO_REMOVE=Python,OpenCV,OCRmyPDF
|
||||||
|
|
||||||
|
# Run the application
|
||||||
|
CMD ["java", "-jar", "/app.jar"]
|
|
@ -1,5 +1,5 @@
|
||||||
# Build jbig2enc in a separate stage
|
# Build jbig2enc in a separate stage
|
||||||
FROM openjdk:17-jdk-slim
|
FROM bellsoft/liberica-openjdk-alpine:17
|
||||||
|
|
||||||
# Copy the application JAR file
|
# Copy the application JAR file
|
||||||
COPY build/libs/*.jar app.jar
|
COPY build/libs/*.jar app.jar
|
|
@ -1,5 +1,5 @@
|
||||||
# Main stage
|
# Main stage
|
||||||
FROM openjdk:17-jdk-slim AS base
|
FROM bellsoft/liberica-openjdk-debian:17 AS base
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y --no-install-recommends \
|
apt-get install -y --no-install-recommends \
|
||||||
libreoffice-core-nogui \
|
libreoffice-core-nogui \
|
||||||
|
|
|
@ -14,13 +14,21 @@ def is_blank_image(image_path, threshold=10, white_percent=99, white_value=255,
|
||||||
blurred_image = cv2.GaussianBlur(image, (blur_size, blur_size), 0)
|
blurred_image = cv2.GaussianBlur(image, (blur_size, blur_size), 0)
|
||||||
|
|
||||||
_, thresholded_image = cv2.threshold(blurred_image, white_value - threshold, white_value, cv2.THRESH_BINARY)
|
_, thresholded_image = cv2.threshold(blurred_image, white_value - threshold, white_value, cv2.THRESH_BINARY)
|
||||||
|
|
||||||
# Calculate the percentage of white pixels in the thresholded image
|
# Calculate the percentage of white pixels in the thresholded image
|
||||||
white_pixels = np.sum(thresholded_image == white_value)
|
white_pixels = 0
|
||||||
total_pixels = thresholded_image.size
|
total_pixels = thresholded_image.size
|
||||||
white_pixel_percentage = (white_pixels / total_pixels) * 100
|
for i in range(0, thresholded_image.shape[0], 2):
|
||||||
|
for j in range(0, thresholded_image.shape[1], 2):
|
||||||
|
if thresholded_image[i, j] == white_value:
|
||||||
|
white_pixels += 1
|
||||||
|
white_pixel_percentage = (white_pixels / (i * thresholded_image.shape[1] + j + 1)) * 100
|
||||||
|
if white_pixel_percentage < white_percent:
|
||||||
|
return False
|
||||||
|
|
||||||
print(f"Page has white pixel percent of {white_pixel_percentage}")
|
print(f"Page has white pixel percent of {white_pixel_percentage}")
|
||||||
return white_pixel_percentage > white_percent
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -20,12 +20,14 @@ public class EndpointConfiguration {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void enableEndpoint(String endpoint) {
|
public void enableEndpoint(String endpoint) {
|
||||||
endpointStatuses.put(endpoint, true);
|
endpointStatuses.put(endpoint, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void disableEndpoint(String endpoint) {
|
public void disableEndpoint(String endpoint) {
|
||||||
logger.info("Disabling {}", endpoint);
|
if(!endpointStatuses.containsKey(endpoint) || endpointStatuses.get(endpoint) != false) {
|
||||||
endpointStatuses.put(endpoint, false);
|
logger.info("Disabling {}", endpoint);
|
||||||
|
endpointStatuses.put(endpoint, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEndpointEnabled(String endpoint) {
|
public boolean isEndpointEnabled(String endpoint) {
|
||||||
|
|
|
@ -74,7 +74,9 @@ public class OtherWebController {
|
||||||
@Hidden
|
@Hidden
|
||||||
public ModelAndView ocrPdfPage() {
|
public ModelAndView ocrPdfPage() {
|
||||||
ModelAndView modelAndView = new ModelAndView("other/ocr-pdf");
|
ModelAndView modelAndView = new ModelAndView("other/ocr-pdf");
|
||||||
modelAndView.addObject("languages", getAvailableTesseractLanguages());
|
List<String> languages = getAvailableTesseractLanguages();
|
||||||
|
Collections.sort(languages);
|
||||||
|
modelAndView.addObject("languages", languages);
|
||||||
modelAndView.addObject("currentPage", "ocr-pdf");
|
modelAndView.addObject("currentPage", "ocr-pdf");
|
||||||
return modelAndView;
|
return modelAndView;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,12 +19,13 @@ $(document).ready(function() {
|
||||||
$('#submitBtn').text('Processing...');
|
$('#submitBtn').text('Processing...');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (override === 'multi' || files.length > 1 && override !== 'single') {
|
if(remoteCall === true) {
|
||||||
await submitMultiPdfForm(url, files);
|
if (override === 'multi' || (!multiple && files.length > 1) && override !== 'single' ) {
|
||||||
} else {
|
await submitMultiPdfForm(url, files);
|
||||||
await handleSingleDownload(url, formData);
|
} else {
|
||||||
|
await handleSingleDownload(url, formData);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#submitBtn').text(originalButtonText);
|
$('#submitBtn').text(originalButtonText);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleDownloadError(error);
|
handleDownloadError(error);
|
||||||
|
|
|
@ -25,33 +25,50 @@ const DraggableUtils = {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.resizable({
|
.resizable({
|
||||||
edges: { left: true, right: true, bottom: true, top: true },
|
edges: { left: true, right: true, bottom: true, top: true },
|
||||||
listeners: {
|
listeners: {
|
||||||
move: (event) => {
|
move: (event) => {
|
||||||
var target = event.target
|
var target = event.target
|
||||||
var x = (parseFloat(target.getAttribute('data-x')) || 0)
|
var x = (parseFloat(target.getAttribute('data-x')) || 0)
|
||||||
var y = (parseFloat(target.getAttribute('data-y')) || 0)
|
var y = (parseFloat(target.getAttribute('data-y')) || 0)
|
||||||
|
|
||||||
// update the element's style
|
// check if control key is pressed
|
||||||
target.style.width = event.rect.width + 'px'
|
if (event.ctrlKey) {
|
||||||
target.style.height = event.rect.height + 'px'
|
const aspectRatio = target.offsetWidth / target.offsetHeight;
|
||||||
|
// preserve aspect ratio
|
||||||
|
let width = event.rect.width;
|
||||||
|
let height = event.rect.height;
|
||||||
|
|
||||||
// translate when resizing from top or left edges
|
if (Math.abs(event.deltaRect.width) >= Math.abs(event.deltaRect.height)) {
|
||||||
x += event.deltaRect.left
|
height = width / aspectRatio;
|
||||||
y += event.deltaRect.top
|
} else {
|
||||||
|
width = height * aspectRatio;
|
||||||
|
}
|
||||||
|
|
||||||
target.style.transform = 'translate(' + x + 'px,' + y + 'px)'
|
event.rect.width = width;
|
||||||
|
event.rect.height = height;
|
||||||
|
}
|
||||||
|
|
||||||
target.setAttribute('data-x', x)
|
target.style.width = event.rect.width + 'px'
|
||||||
target.setAttribute('data-y', y)
|
target.style.height = event.rect.height + 'px'
|
||||||
target.textContent = Math.round(event.rect.width) + '\u00D7' + Math.round(event.rect.height)
|
|
||||||
|
// translate when resizing from top or left edges
|
||||||
|
x += event.deltaRect.left
|
||||||
|
y += event.deltaRect.top
|
||||||
|
|
||||||
|
target.style.transform = 'translate(' + x + 'px,' + y + 'px)'
|
||||||
|
|
||||||
|
target.setAttribute('data-x', x)
|
||||||
|
target.setAttribute('data-y', y)
|
||||||
|
target.textContent = Math.round(event.rect.width) + '\u00D7' + Math.round(event.rect.height)
|
||||||
|
|
||||||
|
this.onInteraction(target);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
this.onInteraction(target);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
modifiers: [
|
modifiers: [
|
||||||
interact.modifiers.restrictSize({
|
interact.modifiers.restrictSize({
|
||||||
min: { width: 50, height: 50 },
|
min: { width: 5, height: 5 },
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
inertia: true,
|
inertia: true,
|
||||||
|
|
|
@ -90,9 +90,11 @@
|
||||||
</dialog>
|
</dialog>
|
||||||
</th:block>
|
</th:block>
|
||||||
|
|
||||||
<th:block th:fragment="fileSelector(name, multiple)" th:with="accept=${accept} ?: '*/*', inputText=${inputText} ?: #{pdfPrompt}, remoteCall=${remoteCall} ?: 'true', notRequired=${notRequired} ?: false">
|
<th:block th:fragment="fileSelector(name, multiple)" th:with="accept=${accept} ?: '*/*', inputText=${inputText} ?: #{pdfPrompt}, remoteCall=${remoteCall} ?: true, notRequired=${notRequired} ?: false">
|
||||||
<script th:inline="javascript">
|
<script th:inline="javascript">
|
||||||
const pdfPasswordPrompt =/*[[#{error.pdfPassword}]]*/ '';
|
const pdfPasswordPrompt =/*[[#{error.pdfPassword}]]*/ '';
|
||||||
|
const multiple = [[${multiple}]] || false;
|
||||||
|
const remoteCall = [[${remoteCall}]] || true;
|
||||||
</script>
|
</script>
|
||||||
<script src="js/downloader.js"></script>
|
<script src="js/downloader.js"></script>
|
||||||
|
|
||||||
|
|
|
@ -130,12 +130,13 @@
|
||||||
// When zoomed out to less than 100%, for some very strange reason,
|
// When zoomed out to less than 100%, for some very strange reason,
|
||||||
// some browsers report devicePixelRatio as less than 1
|
// some browsers report devicePixelRatio as less than 1
|
||||||
// and only part of the canvas is cleared then.
|
// and only part of the canvas is cleared then.
|
||||||
var ratio = Math.max(window.devicePixelRatio || 1, 1);
|
var ratio = Math.max(window.devicePixelRatio || 1, 1);
|
||||||
|
var additionalFactor = 10;
|
||||||
|
|
||||||
|
signaturePadCanvas.width = signaturePadCanvas.offsetWidth * ratio * additionalFactor;
|
||||||
|
signaturePadCanvas.height = signaturePadCanvas.offsetHeight * ratio * additionalFactor;
|
||||||
|
signaturePadCanvas.getContext("2d").scale(ratio * additionalFactor, ratio * additionalFactor);
|
||||||
|
|
||||||
// This part causes the canvas to be cleared
|
|
||||||
signaturePadCanvas.width = signaturePadCanvas.offsetWidth * ratio;
|
|
||||||
signaturePadCanvas.height = signaturePadCanvas.offsetHeight * ratio;
|
|
||||||
signaturePadCanvas.getContext("2d").scale(ratio, ratio);
|
|
||||||
|
|
||||||
// This library does not listen for canvas changes, so after the canvas is automatically
|
// This library does not listen for canvas changes, so after the canvas is automatically
|
||||||
// cleared by the browser, SignaturePad#isEmpty might still return false, even though the
|
// cleared by the browser, SignaturePad#isEmpty might still return false, even though the
|
||||||
|
|
Loading…
Reference in a new issue