diff --git a/.github/workflows/push-docker.yml b/.github/workflows/push-docker.yml index 71f497b6..a08f0f7c 100644 --- a/.github/workflows/push-docker.yml +++ b/.github/workflows/push-docker.yml @@ -8,8 +8,14 @@ on: - main jobs: push: - runs-on: ubuntu-latest + strategy: + matrix: + config: [ + { dockerfile: "./Dockerfile", tagSuffix: "" }, + { dockerfile: "./Dockerfile-ultra-lite", tagSuffix: "-ultra-lite" }, + { dockerfile: "./Dockerfile-lite", tagSuffix: "-lite" } + ] steps: - uses: actions/checkout@v3.5.2 @@ -46,17 +52,10 @@ jobs: username: ${{ github.actor }} password: ${{ github.token }} - - name: Generate tags - id: meta - 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 }},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: Convert repository owner to lowercase + id: repoowner + run: echo "::set-output name=lowercase::$(echo ${{ github.repository_owner }} | awk '{print tolower($0)}')" + - name: Set up QEMU uses: docker/setup-qemu-action@v2.1.0 @@ -64,44 +63,28 @@ jobs: - name: Set up Docker Buildx 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 with: context: . - dockerfile: ./Dockerfile + dockerfile: ${{ matrix.config.dockerfile }} push: true cache-from: type=gha cache-to: type=gha,mode=max tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} 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 - - + \ No newline at end of file diff --git a/Dockerfile-lite b/Dockerfile-lite new file mode 100644 index 00000000..d3968a2a --- /dev/null +++ b/Dockerfile-lite @@ -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"] diff --git a/Dockerfile-ultralite b/Dockerfile-ultra-lite similarity index 86% rename from Dockerfile-ultralite rename to Dockerfile-ultra-lite index 2c87caf7..4960acc0 100644 --- a/Dockerfile-ultralite +++ b/Dockerfile-ultra-lite @@ -1,5 +1,5 @@ # Build jbig2enc in a separate stage -FROM openjdk:17-jdk-slim +FROM bellsoft/liberica-openjdk-alpine:17 # Copy the application JAR file COPY build/libs/*.jar app.jar diff --git a/DockerfileBase b/DockerfileBase index d43f0e65..8a43832f 100644 --- a/DockerfileBase +++ b/DockerfileBase @@ -1,5 +1,5 @@ # Main stage -FROM openjdk:17-jdk-slim AS base +FROM bellsoft/liberica-openjdk-debian:17 AS base RUN apt-get update && \ apt-get install -y --no-install-recommends \ libreoffice-core-nogui \ diff --git a/scripts/detect-blank-pages.py b/scripts/detect-blank-pages.py index 39a065cd..6712dc12 100644 --- a/scripts/detect-blank-pages.py +++ b/scripts/detect-blank-pages.py @@ -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) _, thresholded_image = cv2.threshold(blurred_image, white_value - threshold, white_value, cv2.THRESH_BINARY) - + # 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 - 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}") - return white_pixel_percentage > white_percent + return True + if __name__ == "__main__": diff --git a/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java b/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java index 6bdb0074..92580b43 100644 --- a/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java +++ b/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java @@ -20,12 +20,14 @@ public class EndpointConfiguration { } public void enableEndpoint(String endpoint) { - endpointStatuses.put(endpoint, true); + endpointStatuses.put(endpoint, true); } public void disableEndpoint(String endpoint) { - logger.info("Disabling {}", endpoint); - endpointStatuses.put(endpoint, false); + if(!endpointStatuses.containsKey(endpoint) || endpointStatuses.get(endpoint) != false) { + logger.info("Disabling {}", endpoint); + endpointStatuses.put(endpoint, false); + } } public boolean isEndpointEnabled(String endpoint) { diff --git a/src/main/java/stirling/software/SPDF/controller/web/OtherWebController.java b/src/main/java/stirling/software/SPDF/controller/web/OtherWebController.java index 5a9e93c0..90a18de9 100644 --- a/src/main/java/stirling/software/SPDF/controller/web/OtherWebController.java +++ b/src/main/java/stirling/software/SPDF/controller/web/OtherWebController.java @@ -74,7 +74,9 @@ public class OtherWebController { @Hidden public ModelAndView ocrPdfPage() { ModelAndView modelAndView = new ModelAndView("other/ocr-pdf"); - modelAndView.addObject("languages", getAvailableTesseractLanguages()); + List languages = getAvailableTesseractLanguages(); + Collections.sort(languages); + modelAndView.addObject("languages", languages); modelAndView.addObject("currentPage", "ocr-pdf"); return modelAndView; } diff --git a/src/main/resources/static/js/downloader.js b/src/main/resources/static/js/downloader.js index 17cca631..117d441c 100644 --- a/src/main/resources/static/js/downloader.js +++ b/src/main/resources/static/js/downloader.js @@ -19,12 +19,13 @@ $(document).ready(function() { $('#submitBtn').text('Processing...'); try { - if (override === 'multi' || files.length > 1 && override !== 'single') { - await submitMultiPdfForm(url, files); - } else { - await handleSingleDownload(url, formData); + if(remoteCall === true) { + if (override === 'multi' || (!multiple && files.length > 1) && override !== 'single' ) { + await submitMultiPdfForm(url, files); + } else { + await handleSingleDownload(url, formData); + } } - $('#submitBtn').text(originalButtonText); } catch (error) { handleDownloadError(error); diff --git a/src/main/resources/static/js/draggable-utils.js b/src/main/resources/static/js/draggable-utils.js index f69aaa85..c1662f22 100644 --- a/src/main/resources/static/js/draggable-utils.js +++ b/src/main/resources/static/js/draggable-utils.js @@ -25,33 +25,50 @@ const DraggableUtils = { }, }) .resizable({ - edges: { left: true, right: true, bottom: true, top: true }, - listeners: { - move: (event) => { - var target = event.target - var x = (parseFloat(target.getAttribute('data-x')) || 0) - var y = (parseFloat(target.getAttribute('data-y')) || 0) + edges: { left: true, right: true, bottom: true, top: true }, + listeners: { + move: (event) => { + var target = event.target + var x = (parseFloat(target.getAttribute('data-x')) || 0) + var y = (parseFloat(target.getAttribute('data-y')) || 0) - // update the element's style - target.style.width = event.rect.width + 'px' - target.style.height = event.rect.height + 'px' + // check if control key is pressed + if (event.ctrlKey) { + 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 - x += event.deltaRect.left - y += event.deltaRect.top + if (Math.abs(event.deltaRect.width) >= Math.abs(event.deltaRect.height)) { + height = width / aspectRatio; + } 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.setAttribute('data-y', y) - target.textContent = Math.round(event.rect.width) + '\u00D7' + Math.round(event.rect.height) + target.style.width = event.rect.width + 'px' + target.style.height = event.rect.height + 'px' + + // 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: [ interact.modifiers.restrictSize({ - min: { width: 50, height: 50 }, + min: { width: 5, height: 5 }, }), ], inertia: true, diff --git a/src/main/resources/templates/fragments/common.html b/src/main/resources/templates/fragments/common.html index 92f6f84a..eb079166 100644 --- a/src/main/resources/templates/fragments/common.html +++ b/src/main/resources/templates/fragments/common.html @@ -90,9 +90,11 @@ - + diff --git a/src/main/resources/templates/sign.html b/src/main/resources/templates/sign.html index f2a9f698..667c3f99 100644 --- a/src/main/resources/templates/sign.html +++ b/src/main/resources/templates/sign.html @@ -130,12 +130,13 @@ // When zoomed out to less than 100%, for some very strange reason, // some browsers report devicePixelRatio as less than 1 // 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 // cleared by the browser, SignaturePad#isEmpty might still return false, even though the