diff --git a/.github/workflows/push-docker-v2.yml b/.github/workflows/push-docker-v2.yml deleted file mode 100644 index f82564417..000000000 --- a/.github/workflows/push-docker-v2.yml +++ /dev/null @@ -1,245 +0,0 @@ -name: Push Docker Image - V2 Branch - -on: - workflow_dispatch: - push: - branches: - - V2-master - -# cancel in-progress jobs if a new job is triggered -# This is useful to avoid running multiple builds for the same branch if a new commit is pushed -# or a pull request is updated. -# It helps to save resources and time by ensuring that only the latest commit is built and tested -# This is particularly useful for long-running jobs that may take a while to complete. -# The `group` is set to a combination of the workflow name, event name, and branch name. -# This ensures that jobs are grouped by the workflow and branch, allowing for cancellation of -# in-progress jobs when a new commit is pushed to the same branch or a new pull request is opened. -concurrency: - group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref_name || github.ref }} - cancel-in-progress: true - -permissions: - contents: read - -jobs: - push: - if: ${{ vars.CI_PROFILE != 'lite' }} - runs-on: ubuntu-24.04-8core - permissions: - packages: write - id-token: write - steps: - - name: Harden Runner - uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 - with: - egress-policy: audit - - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - - name: Set up JDK 21 - uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 - with: - java-version: "21" - distribution: "temurin" - - - uses: gradle/actions/setup-gradle@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # v5.0.0 - with: - gradle-version: 8.14 - - - name: Set up Docker Buildx - id: buildx - uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 - - - name: Get version number - id: versionNumber - run: echo "versionNumber=$(./gradlew printVersion --quiet | tail -1)" >> $GITHUB_OUTPUT - - - name: Install cosign - if: github.ref == 'refs/heads/V2-master' - uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # v3.10.0 - with: - cosign-release: "v2.4.1" - - - name: Login to Docker Hub - uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 - with: - username: ${{ secrets.DOCKER_HUB_USERNAME }} - password: ${{ secrets.DOCKER_HUB_API }} - - - name: Login to GitHub Container Registry - uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ github.token }} - - - name: Set up QEMU - uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0 - - - name: Convert repository owner to lowercase - id: repoowner - run: echo "lowercase=$(echo ${{ github.repository_owner }} | awk '{print tolower($0)}')" >> $GITHUB_OUTPUT - - - name: Generate tags for latest (V2-master branch - production) - id: meta - uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0 - if: github.ref == 'refs/heads/V2-master' - with: - images: | - ${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf - ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf - ghcr.io/${{ steps.repoowner.outputs.lowercase }}/stirling-pdf - ${{ secrets.DOCKER_HUB_ORG_USERNAME }}/stirling-pdf - tags: | - type=raw,value=${{ steps.versionNumber.outputs.versionNumber }} - type=raw,value=latest - - - name: Generate tags for latest (V1_V2_merge branch - test) - id: meta-test - uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0 - if: github.ref == 'refs/heads/V1_V2_merge' - with: - images: | - ghcr.io/stirling-tools/stirling-pdf-test - tags: | - type=raw,value=${{ steps.versionNumber.outputs.versionNumber }} - type=raw,value=latest - - - name: Build and push Unified Dockerfile (latest variant) - id: build-push-latest - uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 - with: - builder: ${{ steps.buildx.outputs.name }} - context: . - file: ./docker/embedded/Dockerfile - push: true - cache-from: type=gha - cache-to: type=gha,mode=max - tags: ${{ github.ref == 'refs/heads/V2-master' && steps.meta.outputs.tags || steps.meta-test.outputs.tags }} - labels: ${{ github.ref == 'refs/heads/V2-master' && steps.meta.outputs.labels || steps.meta-test.outputs.labels }} - build-args: VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }} - platforms: linux/amd64,linux/arm64/v8 - provenance: true - sbom: true - - - name: Sign regular images - if: github.ref == 'refs/heads/V2-master' - env: - DIGEST: ${{ steps.build-push-latest.outputs.digest }} - TAGS: ${{ steps.meta.outputs.tags }} - COSIGN_PRIVATE_KEY: ${{ secrets.COSIGN_PRIVATE_KEY }} - COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }} - run: | - echo "$TAGS" | tr ',' '\n' | while read -r tag; do - cosign sign --yes \ - --key env://COSIGN_PRIVATE_KEY \ - "${tag}@${DIGEST}" - done - - - name: Generate tags for latest-fat (V2-master branch - production) - id: meta-fat - uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0 - if: github.ref == 'refs/heads/V2-master' - with: - images: | - ${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf - ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf - ghcr.io/${{ steps.repoowner.outputs.lowercase }}/stirling-pdf - ${{ secrets.DOCKER_HUB_ORG_USERNAME }}/stirling-pdf - tags: | - type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-fat - type=raw,value=latest-fat - - - name: Generate tags for latest-fat (V1_V2_merge branch - test) - id: meta-fat-test - uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0 - if: github.ref == 'refs/heads/V1_V2_merge' - with: - images: | - ghcr.io/stirling-tools/stirling-pdf-test - tags: | - type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-fat - type=raw,value=latest-fat - - - name: Build and push Unified Dockerfile (fat variant) - id: build-push-fat - uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 - with: - builder: ${{ steps.buildx.outputs.name }} - context: . - file: ./docker/embedded/Dockerfile.fat - push: true - cache-from: type=gha - cache-to: type=gha,mode=max - tags: ${{ github.ref == 'refs/heads/V2-master' && steps.meta-fat.outputs.tags || steps.meta-fat-test.outputs.tags }} - labels: ${{ github.ref == 'refs/heads/V2-master' && steps.meta-fat.outputs.labels || steps.meta-fat-test.outputs.labels }} - build-args: VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }} - platforms: linux/amd64,linux/arm64/v8 - provenance: true - sbom: true - - - name: Sign fat images - if: github.ref == 'refs/heads/V2-master' - env: - DIGEST: ${{ steps.build-push-fat.outputs.digest }} - TAGS: ${{ steps.meta-fat.outputs.tags }} - COSIGN_PRIVATE_KEY: ${{ secrets.COSIGN_PRIVATE_KEY }} - COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }} - run: | - echo "$TAGS" | tr ',' '\n' | while read -r tag; do - cosign sign --key env://COSIGN_PRIVATE_KEY --yes "${tag}@${DIGEST}" - done - - - name: Generate tags for ultra-lite (V2-master branch - production) - id: meta-lite - uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0 - if: github.ref == 'refs/heads/V2-master' - with: - images: | - ${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf - ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf - ghcr.io/${{ steps.repoowner.outputs.lowercase }}/stirling-pdf - ${{ secrets.DOCKER_HUB_ORG_USERNAME }}/stirling-pdf - tags: | - type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-ultra-lite - type=raw,value=latest-ultra-lite - - - name: Generate tags for ultra-lite (V1_V2_merge branch - test) - id: meta-lite-test - uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0 - if: github.ref == 'refs/heads/V1_V2_merge' - with: - images: | - ghcr.io/stirling-tools/stirling-pdf-test - tags: | - type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-ultra-lite - type=raw,value=latest-ultra-lite - - - name: Build and push Unified Dockerfile (ultra-lite variant) - id: build-push-lite - uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 - with: - builder: ${{ steps.buildx.outputs.name }} - context: . - file: ./docker/embedded/Dockerfile.ultra-lite - push: true - cache-from: type=gha - cache-to: type=gha,mode=max - tags: ${{ github.ref == 'refs/heads/V2-master' && steps.meta-lite.outputs.tags || steps.meta-lite-test.outputs.tags }} - labels: ${{ github.ref == 'refs/heads/V2-master' && steps.meta-lite.outputs.labels || steps.meta-lite-test.outputs.labels }} - build-args: VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }} - platforms: linux/amd64,linux/arm64/v8 - provenance: true - sbom: true - - - name: Sign ultra-lite images - if: github.ref == 'refs/heads/V2-master' - env: - DIGEST: ${{ steps.build-push-lite.outputs.digest }} - TAGS: ${{ steps.meta-lite.outputs.tags }} - COSIGN_PRIVATE_KEY: ${{ secrets.COSIGN_PRIVATE_KEY }} - COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }} - run: | - echo "$TAGS" | tr ',' '\n' | while read -r tag; do - cosign sign --key env://COSIGN_PRIVATE_KEY --yes "${tag}@${DIGEST}" - done diff --git a/.github/workflows/push-docker.yml b/.github/workflows/push-docker.yml index 6d0afe96e..736ba0f97 100644 --- a/.github/workflows/push-docker.yml +++ b/.github/workflows/push-docker.yml @@ -6,6 +6,8 @@ on: branches: - master - main + - V2-master + - testMain # cancel in-progress jobs if a new job is triggered # This is useful to avoid running multiple builds for the same branch if a new commit is pushed @@ -25,7 +27,7 @@ permissions: jobs: push: if: ${{ vars.CI_PROFILE != 'lite' }} - runs-on: ubuntu-latest + runs-on: ubuntu-24.04-8core permissions: packages: write id-token: write @@ -47,18 +49,6 @@ jobs: with: gradle-version: 8.14 - - name: Run Gradle Command - run: ./gradlew clean build - env: - DISABLE_ADDITIONAL_FEATURES: true - STIRLING_PDF_DESKTOP_UI: false - - - name: Install cosign - if: github.ref == 'refs/heads/master' - uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0 - with: - cosign-release: "v2.4.1" - - name: Set up Docker Buildx id: buildx uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 @@ -67,6 +57,12 @@ jobs: id: versionNumber run: echo "versionNumber=$(./gradlew printVersion --quiet | tail -1)" >> $GITHUB_OUTPUT + - name: Install cosign + if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/V2-master' + uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0 + with: + cosign-release: "v2.4.1" + - name: Login to Docker Hub uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 with: @@ -87,10 +83,9 @@ jobs: id: repoowner run: echo "lowercase=$(echo ${{ github.repository_owner }} | awk '{print tolower($0)}')" >> $GITHUB_OUTPUT - - name: Generate tags + - name: Generate tags for latest id: meta uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0 - if: github.ref != 'refs/heads/main' with: images: | ${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf @@ -98,13 +93,13 @@ jobs: ghcr.io/${{ steps.repoowner.outputs.lowercase }}/stirling-pdf ${{ secrets.DOCKER_HUB_ORG_USERNAME }}/stirling-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=${{ steps.versionNumber.outputs.versionNumber }},enable=${{ github.ref == 'refs/heads/master' || github.ref == 'refs/heads/V2-master' }} + type=raw,value=latest,enable=${{ github.ref == 'refs/heads/master' || github.ref == 'refs/heads/V2-master' }} + type=raw,value=alpha,enable=${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/testMain' }} - - name: Build and push main Dockerfile - id: build-push-regular + - name: Build and push Unified Dockerfile (latest variant) + id: build-push-latest uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 - if: github.ref != 'refs/heads/main' with: builder: ${{ steps.buildx.outputs.name }} context: . @@ -120,9 +115,9 @@ jobs: sbom: true - name: Sign regular images - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/V2-master' env: - DIGEST: ${{ steps.build-push-regular.outputs.digest }} + DIGEST: ${{ steps.build-push-latest.outputs.digest }} TAGS: ${{ steps.meta.outputs.tags }} COSIGN_PRIVATE_KEY: ${{ secrets.COSIGN_PRIVATE_KEY }} COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }} @@ -133,10 +128,10 @@ jobs: "${tag}@${DIGEST}" done - - name: Generate tags ultra-lite - id: meta2 + - name: Generate tags for latest-fat + id: meta-fat uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0 - if: github.ref != 'refs/heads/main' + if: github.ref != 'refs/heads/main' && github.ref != 'refs/heads/testMain' with: images: | ${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf @@ -144,43 +139,13 @@ jobs: ghcr.io/${{ steps.repoowner.outputs.lowercase }}/stirling-pdf ${{ secrets.DOCKER_HUB_ORG_USERNAME }}/stirling-pdf tags: | - type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-ultra-lite,enable=${{ github.ref == 'refs/heads/master' }} - type=raw,value=latest-ultra-lite,enable=${{ github.ref == 'refs/heads/master' }} + type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-fat,enable=${{ github.ref == 'refs/heads/master' || github.ref == 'refs/heads/V2-master' }} + type=raw,value=latest-fat,enable=${{ github.ref == 'refs/heads/master' || github.ref == 'refs/heads/V2-master' }} - - name: Build and push Dockerfile-ultra-lite - id: build-push-lite - uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 - if: github.ref != 'refs/heads/main' - with: - context: . - file: ./docker/embedded/Dockerfile.ultra-lite - push: true - cache-from: type=gha - cache-to: type=gha,mode=max - tags: ${{ steps.meta2.outputs.tags }} - labels: ${{ steps.meta2.outputs.labels }} - build-args: VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }} - platforms: linux/amd64,linux/arm64/v8 - provenance: true - sbom: true - - - name: Generate tags fat - id: meta3 - uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0 - with: - images: | - ${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf - ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf - ghcr.io/${{ steps.repoowner.outputs.lowercase }}/stirling-pdf - ${{ secrets.DOCKER_HUB_ORG_USERNAME }}/stirling-pdf - tags: | - type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-fat,enable=${{ github.ref == 'refs/heads/master' }} - type=raw,value=latest-fat,enable=${{ github.ref == 'refs/heads/master' }} - type=raw,value=alpha,enable=${{ github.ref == 'refs/heads/main' }} - - - name: Build and push main Dockerfile fat + - name: Build and push Unified Dockerfile (fat variant) id: build-push-fat uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 + if: github.ref != 'refs/heads/main' && github.ref != 'refs/heads/testMain' with: builder: ${{ steps.buildx.outputs.name }} context: . @@ -188,18 +153,62 @@ jobs: push: true cache-from: type=gha cache-to: type=gha,mode=max - tags: ${{ steps.meta3.outputs.tags }} - labels: ${{ steps.meta3.outputs.labels }} + tags: ${{ steps.meta-fat.outputs.tags }} + labels: ${{ steps.meta-fat.outputs.labels }} build-args: VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }} platforms: linux/amd64,linux/arm64/v8 provenance: true sbom: true - name: Sign fat images - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/V2-master' env: DIGEST: ${{ steps.build-push-fat.outputs.digest }} - TAGS: ${{ steps.meta3.outputs.tags }} + TAGS: ${{ steps.meta-fat.outputs.tags }} + COSIGN_PRIVATE_KEY: ${{ secrets.COSIGN_PRIVATE_KEY }} + COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }} + run: | + echo "$TAGS" | tr ',' '\n' | while read -r tag; do + cosign sign --key env://COSIGN_PRIVATE_KEY --yes "${tag}@${DIGEST}" + done + + - name: Generate tags for ultra-lite + id: meta-lite + uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0 + if: github.ref != 'refs/heads/main' && github.ref != 'refs/heads/testMain' + with: + images: | + ${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf + ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf + ghcr.io/${{ steps.repoowner.outputs.lowercase }}/stirling-pdf + ${{ secrets.DOCKER_HUB_ORG_USERNAME }}/stirling-pdf + tags: | + type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-ultra-lite,enable=${{ github.ref == 'refs/heads/master' || github.ref == 'refs/heads/V2-master' }} + type=raw,value=latest-ultra-lite,enable=${{ github.ref == 'refs/heads/master' || github.ref == 'refs/heads/V2-master' }} + + - name: Build and push Unified Dockerfile (ultra-lite variant) + id: build-push-lite + uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 + if: github.ref != 'refs/heads/main' && github.ref != 'refs/heads/testMain' + with: + builder: ${{ steps.buildx.outputs.name }} + context: . + file: ./docker/embedded/Dockerfile.ultra-lite + push: true + cache-from: type=gha + cache-to: type=gha,mode=max + tags: ${{ steps.meta-lite.outputs.tags }} + labels: ${{ steps.meta-lite.outputs.labels }} + build-args: VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }} + platforms: linux/amd64,linux/arm64/v8 + provenance: true + sbom: true + + - name: Sign ultra-lite images + if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/V2-master' + env: + DIGEST: ${{ steps.build-push-lite.outputs.digest }} + TAGS: ${{ steps.meta-lite.outputs.tags }} COSIGN_PRIVATE_KEY: ${{ secrets.COSIGN_PRIVATE_KEY }} COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }} run: | diff --git a/app/core/src/main/java/stirling/software/SPDF/controller/api/MergeController.java b/app/core/src/main/java/stirling/software/SPDF/controller/api/MergeController.java index e4986d106..b93f5365a 100644 --- a/app/core/src/main/java/stirling/software/SPDF/controller/api/MergeController.java +++ b/app/core/src/main/java/stirling/software/SPDF/controller/api/MergeController.java @@ -3,6 +3,7 @@ package stirling.software.SPDF.controller.api; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; @@ -27,7 +28,6 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.multipart.MultipartFile; -import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; import io.swagger.v3.oas.annotations.Operation; @@ -277,7 +277,7 @@ public class MergeController { "This endpoint merges multiple PDF files into a single PDF file. The merged" + " file will contain all pages from the input files in the order they were" + " provided. Input:PDF Output:PDF Type:MISO") - public ResponseEntity mergePdfs( + public ResponseEntity mergePdfs( @ModelAttribute MergePdfsRequest request, @RequestParam(value = "fileOrder", required = false) String fileOrder) throws IOException { @@ -304,8 +304,6 @@ public class MergeController { request.getSortType())); // Sort files based on requested sort type } - ResponseEntity response; - try (TempFile mt = new TempFile(tempFileManager, ".pdf")) { PDFMergerUtility mergerUtility = new PDFMergerUtility(); @@ -399,7 +397,7 @@ public class MergeController { String mergedFileName = GeneralUtils.generateFilename(firstFilename, "_merged_unsigned.pdf"); - response = WebResponseUtils.pdfFileToWebResponse(outputTempFile, mergedFileName); - return response; + byte[] pdfBytes = Files.readAllBytes(outputTempFile.getPath()); + return WebResponseUtils.bytesToWebResponse(pdfBytes, mergedFileName); } } diff --git a/app/core/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySectionsController.java b/app/core/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySectionsController.java index 44b350e9e..b755a1d53 100644 --- a/app/core/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySectionsController.java +++ b/app/core/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySectionsController.java @@ -1,7 +1,7 @@ package stirling.software.SPDF.controller.api; +import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.OutputStream; import java.nio.file.Files; import java.util.*; import java.util.stream.IntStream; @@ -19,40 +19,37 @@ import org.apache.pdfbox.util.Matrix; 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 org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import stirling.software.SPDF.config.swagger.MultiFileResponse; import stirling.software.SPDF.model.SplitTypes; import stirling.software.SPDF.model.api.SplitPdfBySectionsRequest; +import stirling.software.common.annotations.AutoJobPostMapping; +import stirling.software.common.annotations.api.GeneralApi; import stirling.software.common.service.CustomPDFDocumentFactory; import stirling.software.common.util.ExceptionUtils; import stirling.software.common.util.GeneralUtils; -import stirling.software.common.util.PDFService; import stirling.software.common.util.TempFile; import stirling.software.common.util.TempFileManager; import stirling.software.common.util.WebResponseUtils; +@GeneralApi @Slf4j -@RestController -@RequestMapping("/api/v1/general") -@Tag(name = "General", description = "General APIs") @RequiredArgsConstructor public class SplitPdfBySectionsController { private final CustomPDFDocumentFactory pdfDocumentFactory; private final TempFileManager tempFileManager; - private final PDFService pdfService; - @PostMapping(value = "/split-pdf-by-sections", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @AutoJobPostMapping( + consumes = MediaType.MULTIPART_FORM_DATA_VALUE, + value = "/split-pdf-by-sections") + @MultiFileResponse @Operation( summary = "Split PDF pages into smaller sections", description = @@ -60,8 +57,8 @@ public class SplitPdfBySectionsController { + " which page to split, and how to split" + " ( halves, thirds, quarters, etc.), both vertically and horizontally." + " Input:PDF Output:ZIP-PDF Type:SISO") - public ResponseEntity splitPdf( - @ModelAttribute SplitPdfBySectionsRequest request) throws Exception { + public ResponseEntity splitPdf(@ModelAttribute SplitPdfBySectionsRequest request) + throws Exception { MultipartFile file = request.getFileInput(); String pageNumbers = request.getPageNumbers(); SplitTypes splitMode = @@ -80,9 +77,10 @@ public class SplitPdfBySectionsController { String filename = GeneralUtils.generateFilename(file.getOriginalFilename(), "_split"); if (merge) { - TempFile tempFile = new TempFile(tempFileManager, ".pdf"); - try (PDDocument mergedDoc = pdfDocumentFactory.createNewDocument(); - OutputStream out = Files.newOutputStream(tempFile.getPath())) { + try (PDDocument mergedDoc = + pdfDocumentFactory.createNewDocumentBasedOnOldDocument( + sourceDocument); + ByteArrayOutputStream baos = new ByteArrayOutputStream()) { LayerUtility layerUtility = new LayerUtility(mergedDoc); for (int pageIndex = 0; pageIndex < sourceDocument.getNumberOfPages(); @@ -99,12 +97,9 @@ public class SplitPdfBySectionsController { addPageToTarget(sourceDocument, pageIndex, mergedDoc, layerUtility); } } - mergedDoc.save(out); - } catch (IOException e) { - log.error("Error creating merged PDF document", e); - throw e; + mergedDoc.save(baos); + return WebResponseUtils.baosToWebResponse(baos, filename + ".pdf"); } - return WebResponseUtils.pdfFileToWebResponse(tempFile, filename + ".pdf"); } else { TempFile zipTempFile = new TempFile(tempFileManager, ".zip"); try (ZipOutputStream zipOut = @@ -163,7 +158,9 @@ public class SplitPdfBySectionsController { log.error("Error creating ZIP file with split PDF sections", e); throw e; } - return WebResponseUtils.zipFileToWebResponse(zipTempFile, filename + ".zip"); + byte[] zipBytes = Files.readAllBytes(zipTempFile.getPath()); + return WebResponseUtils.bytesToWebResponse( + zipBytes, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM); } } catch (Exception e) { log.error("Error splitting PDF file: {}", file.getOriginalFilename(), e); diff --git a/docker/embedded/Dockerfile b/docker/embedded/Dockerfile index f2b7f979f..515fdf1b9 100644 --- a/docker/embedded/Dockerfile +++ b/docker/embedded/Dockerfile @@ -54,7 +54,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ tesseract-ocr tesseract-ocr-eng tesseract-ocr-deu tesseract-ocr-fra \ tesseract-ocr-por tesseract-ocr-chi-sim \ libcairo2 libpango-1.0-0 libpangoft2-1.0-0 libgdk-pixbuf-2.0-0 \ - gosu unpaper \ + gosu unpaper qpdf \ # AWT headless support (required for some Java graphics operations) libfreetype6 libfontconfig1 libx11-6 libxt6 libxext6 libxrender1 libxtst6 libxi6 \ libxinerama1 libxkbcommon0 libxkbfile1 libsm6 libice6 \ diff --git a/docker/embedded/Dockerfile.fat b/docker/embedded/Dockerfile.fat index 8ea67a12e..721ac21a0 100644 --- a/docker/embedded/Dockerfile.fat +++ b/docker/embedded/Dockerfile.fat @@ -54,7 +54,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ tesseract-ocr tesseract-ocr-eng tesseract-ocr-deu tesseract-ocr-fra \ tesseract-ocr-por tesseract-ocr-chi-sim \ libcairo2 libpango-1.0-0 libpangoft2-1.0-0 libgdk-pixbuf-2.0-0 \ - gosu unpaper \ + gosu unpaper qpdf \ # Extra fonts for fat/air-gapped version fonts-dejavu fonts-liberation fonts-noto fonts-noto-cjk fonts-noto-color-emoji \ fonts-freefont-ttf fonts-terminus fonts-linuxlibertine \