{"id":51110673,"url":"https://github.com/elecbug/apdf","last_synced_at":"2026-06-24T17:30:24.498Z","repository":{"id":366467473,"uuid":"1276402368","full_name":"elecbug/apdf","owner":"elecbug","description":"APDF, a lightweight internal PDF utility server","archived":false,"fork":false,"pushed_at":"2026-06-22T01:59:31.000Z","size":23,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-06-22T02:27:45.924Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/elecbug.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-21T23:59:01.000Z","updated_at":"2026-06-22T01:59:34.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/elecbug/apdf","commit_stats":null,"previous_names":["elecbug/apdf"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/elecbug/apdf","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elecbug%2Fapdf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elecbug%2Fapdf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elecbug%2Fapdf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elecbug%2Fapdf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/elecbug","download_url":"https://codeload.github.com/elecbug/apdf/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elecbug%2Fapdf/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34743465,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-24T02:00:07.484Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-24T17:30:23.771Z","updated_at":"2026-06-24T17:30:24.493Z","avatar_url":"https://github.com/elecbug.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# APDF\n\nAPDF is a lightweight internal-network PDF utility server for assembling and editing PDF files.\n\nIt is designed for short-lived internal use, not as a long-term document storage system.\n\n## What APDF can do\n\nAPDF currently provides two main workflows:\n\n* **Assemble PDFs**\n\n  * Upload multiple PDF files into a browser-session source cache.\n  * Select page ranges from uploaded sources.\n  * Build a new PDF from the selected ranges in order.\n  * Download the generated PDF using a result code.\n\n* **Edit a PDF**\n\n  * Load a PDF file in the Edit page.\n  * Preview the PDF in the browser.\n  * Queue edit operations.\n  * Apply edits and continue previewing the edited result.\n  * Download the edited PDF directly.\n\nCurrent edit operations include:\n\n* Insert blank pages\n* Insert PNG/JPEG/WebP images as PDF pages\n* Rotate selected pages\n* Delete selected pages\n* Move selected pages\n\n## Run\n\nStart APDF with Docker Compose:\n\n```bash\ndocker compose up -d --build\n```\n\nOpen APDF in a browser:\n\n```text\nhttp://SERVER_IP:8000\n```\n\n## Restart\n\nRestart the running service:\n\n```bash\ndocker compose restart\n```\n\nAfter code changes, rebuild and restart:\n\n```bash\ndocker compose up -d --build\n```\n\n## Basic usage\n\n### 1. Assemble PDFs\n\nOpen the Assemble page:\n\n```text\nhttp://SERVER_IP:8000/assemble\n```\n\nSteps:\n\n1. Click **Add PDFs to Sources**.\n2. Select one or more PDF files.\n3. In **Source PDFs**, set the first and last page numbers.\n4. Click **Add** to add that page range to the Assembly list.\n5. Repeat until the Assembly list contains all desired ranges.\n6. Reorder or remove Assembly items if needed.\n7. Click **Build PDF**.\n8. Open the result page and download the generated PDF.\n\nPage numbers are 1-based.\n\n### 2. Edit a PDF\n\nOpen the Edit page:\n\n```text\nhttp://SERVER_IP:8000/edit\n```\n\nSteps:\n\n1. Click **Choose PDF to Edit**.\n2. Select a PDF file.\n3. Preview the PDF in the left panel.\n4. Choose an operation in the right panel:\n\n   * **Blank Page**\n   * **Insert Image**\n   * **Rotate Pages**\n   * **Delete Pages**\n   * **Move Pages**\n5. Configure the operation.\n6. Click the operation add button to add it to the edit queue.\n7. Click **Apply Edits**.\n8. The preview updates to the edited PDF.\n9. Click **Download PDF** to download the latest edited result.\n\nThe Edit page is designed for iterative editing. After applying edits, the edited PDF becomes the new current preview target.\n\n## Smoke check\n\nAPDF includes a lightweight endpoint smoke checker for verifying that the current URL, JSON, and multipart contracts still work after refactoring.\n\nRun APDF first:\n\n```bash\ndocker compose up -d --build\n```\n\nThen run the checker with a small PDF file:\n\n```bash\npython tools/apdf_smoke_check.py --base-url http://127.0.0.1:8000 --pdf test.pdf\n```\n\nThe checker covers the current public workflow endpoints:\n\n```text\nGET    /\nGET    /assemble\nGET    /edit\nGET    /api/clients/{client_id}/sources\nPOST   /api/clients/{client_id}/sources\nDELETE /api/clients/{client_id}/sources/{source_id}\nDELETE /api/clients/{client_id}/sources\nPOST   /compose\nPOST   /edit/apply\nGET    /job/{code}\nPOST   /lookup\nGET    /download/{code}/{filename}\nPOST   /delete-job/{code}\n```\n\nFor `/edit/apply`, it checks blank-page insertion, image-page insertion, rotation, page deletion, page movement, and a combined edit queue.\n\nIf legacy standalone endpoints have been removed, this optional check verifies they return `404`:\n\n```bash\npython tools/apdf_smoke_check.py --base-url http://127.0.0.1:8000 --pdf test.pdf --expect-legacy-removed\n```\n\n## Result codes\n\nGenerated outputs are stored as short-lived jobs.\n\nA result code can be used to reopen a generated result:\n\n1. Enter the result code in the top-right input box.\n2. Click **Open**.\n3. Download or delete the result from the result page.\n\nResult codes are not intended as permanent links.\n\n## Storage model\n\nAPDF uses two short-lived storage areas:\n\n```text\napp/data/\n├── clients/   # browser-session source cache\n└── jobs/      # generated output jobs\n```\n\n### Browser-session source cache\n\nThe Assemble page uses a browser-session source cache:\n\n1. The browser creates and stores `apdf_client_id` in `localStorage`.\n2. Uploaded source PDFs are stored under:\n\n```text\napp/data/clients/\u003cclient_id\u003e/sources/\n```\n\n3. If the user reloads the page or goes back to the page, APDF restores the source list while the server-side cache is still alive.\n4. The browser cannot repopulate file inputs after reload, so APDF restores the uploaded source list from the server-side cache instead.\n\nThis cache is temporary and browser-bound.\n\n### Job output storage\n\nGenerated outputs are stored as jobs under:\n\n```text\napp/data/jobs/\n```\n\nJobs are accessed by result code and expire automatically.\n\n## Default expiration\n\nDefault expiration values:\n\n```text\nJob outputs:          2 hours\nClient source cache:  6 hours\n```\n\nThese values can be changed with environment variables.\n\n## Environment variables\n\n```text\nAPDF_JOB_EXPIRE_SECONDS       Job output lifetime in seconds\nAPDF_CLIENT_EXPIRE_SECONDS    Browser-session source cache lifetime in seconds\nAPDF_MAX_INLINE_BYTES         Inline processing size threshold\nAPDF_MAX_INLINE_PAGES         Inline processing page threshold\nAPDF_CODE_LENGTH              Result code length\nAPDF_CODE_ALPHABET            Characters used for result codes\nAPDF_DATA_DIR                 Data directory path\nAPDF_FONTS_DIR                Font directory path\nAPDF_ACCESS_LOG_DIR           Access log directory path\nAPDF_LOG_TZ                   Log timezone\n```\n\n## PDF.js files\n\nThe Edit page uses PDF.js for browser-side PDF preview.\n\nThe expected static file paths are:\n\n```text\napp/static/pdfjs/pdf.mjs\napp/static/pdfjs/pdf.worker.mjs\n```\n\nIf the Dockerfile uses `pdfjs-dist`, these files can be copied automatically during image build.\n\n## Notes\n\n* APDF is intended for internal-network use.\n* APDF is not a long-term document management system.\n* Uploaded source files and generated outputs expire automatically.\n* For sensitive documents, use APDF only on a trusted internal network.\n* Result codes are convenient access codes, not strong authentication.\n* Delete generated jobs manually when they are no longer needed.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felecbug%2Fapdf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Felecbug%2Fapdf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felecbug%2Fapdf/lists"}