From 1a73bd6b1018b6852412d043afaf38efee126d03 Mon Sep 17 00:00:00 2001 From: TTigger Date: Mon, 13 Jul 2026 22:31:37 +0800 Subject: [PATCH] =?UTF-8?q?feat(pdf):=20PDF=20=E5=B7=A5=E5=85=B7=20?= =?UTF-8?q?=E2=80=94=20=E5=90=88=E4=BD=B5=EF=BC=8F=E6=8B=86=E5=88=86?= =?UTF-8?q?=EF=BC=8F=E6=97=8B=E8=BD=89=EF=BC=88stage=20C=20item=204?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 範圍照 handoff 與使用者確認:合併+拆分+旋轉,砍掉壓縮。 pdf-lib(新依賴)純前端處理,檔案不離開瀏覽器。 - src/lib/pdfPages.ts:頁碼範圍解析(TDD 5 tests)——「1-3, 7, 10-12」, 全形逗號/頓號容忍,超界或反向整串拒絕不猜意圖。 - src/lib/pdfOps.ts:合併/抽頁/旋轉(TDD 5 tests,用 pdf-lib 現做 PDF 驗頁數與旋轉角);旋轉在原角度上累加 mod 360。 - src/tools/PdfStudio.tsx:三分頁——合併(多檔排序上移下移)、 拆分(範圍抽頁)、旋轉(90/180/270,可指定頁);壞檔與加密檔 有明確錯誤訊息。 - 中英頁面、tools.ts 註冊(實用工具 📄)、OG 卡 74 張。 - e2e:現做 PDF 上傳,下載檔用 pdf-lib 回讀驗證頁數與旋轉角(+3), 含錯誤頁碼的停用與提示。 測試:typecheck:api ✓ / 551 unit ✓(+10)/ 65 e2e ✓(+3) 純前端,不碰 api/。 Co-Authored-By: Claude Fable 5 --- e2e/pdf-studio.spec.ts | 68 +++++++ package-lock.json | 55 ++++++ package.json | 1 + public/og/en/pdf-studio.png | Bin 0 -> 35559 bytes public/og/zh/pdf-studio.png | Bin 0 -> 34184 bytes src/data/tools.ts | 1 + src/lib/__tests__/pdfOps.test.ts | 57 ++++++ src/lib/__tests__/pdfPages.test.ts | 31 +++ src/lib/pdfOps.ts | 37 ++++ src/lib/pdfPages.ts | 29 +++ src/pages/en/tools/pdf-studio.astro | 12 ++ src/pages/tools/pdf-studio.astro | 9 + src/tools/PdfStudio.tsx | 280 ++++++++++++++++++++++++++++ 13 files changed, 580 insertions(+) create mode 100644 e2e/pdf-studio.spec.ts create mode 100644 public/og/en/pdf-studio.png create mode 100644 public/og/zh/pdf-studio.png create mode 100644 src/lib/__tests__/pdfOps.test.ts create mode 100644 src/lib/__tests__/pdfPages.test.ts create mode 100644 src/lib/pdfOps.ts create mode 100644 src/lib/pdfPages.ts create mode 100644 src/pages/en/tools/pdf-studio.astro create mode 100644 src/pages/tools/pdf-studio.astro create mode 100644 src/tools/PdfStudio.tsx diff --git a/e2e/pdf-studio.spec.ts b/e2e/pdf-studio.spec.ts new file mode 100644 index 0000000..f48d65f --- /dev/null +++ b/e2e/pdf-studio.spec.ts @@ -0,0 +1,68 @@ +import { test, expect } from '@playwright/test'; +import { PDFDocument } from 'pdf-lib'; +import { readFileSync } from 'node:fs'; +import { waitForIslands } from './helpers'; + +async function makePdf(pages: number): Promise { + const doc = await PDFDocument.create(); + for (let i = 0; i < pages; i++) doc.addPage([200, 300]); + return Buffer.from(await doc.save()); +} + +async function downloadedPageCount(path: string): Promise { + return (await PDFDocument.load(readFileSync(path))).getPageCount(); +} + +test('合併兩份 PDF → 下載檔頁數相加', async ({ page }) => { + await page.goto('/tools/pdf-studio'); + await waitForIslands(page); + + await page.locator('input[type="file"]').setInputFiles([ + { name: 'a.pdf', mimeType: 'application/pdf', buffer: await makePdf(3) }, + { name: 'b.pdf', mimeType: 'application/pdf', buffer: await makePdf(2) }, + ]); + await expect(page.getByText('a.pdf')).toBeVisible(); + await expect(page.getByText('3 頁')).toBeVisible(); + + const downloadPromise = page.waitForEvent('download'); + await page.getByRole('button', { name: '合併下載' }).click(); + const download = await downloadPromise; + expect(download.suggestedFilename()).toBe('a-merged.pdf'); + expect(await downloadedPageCount((await download.path())!)).toBe(5); +}); + +test('拆分:抽出 2,4 兩頁;亂打頁碼給明確錯誤', async ({ page }) => { + await page.goto('/tools/pdf-studio'); + await waitForIslands(page); + await page.getByRole('button', { name: '拆分' }).click(); + + await page.locator('input[type="file"]').setInputFiles({ name: 'doc.pdf', mimeType: 'application/pdf', buffer: await makePdf(5) }); + await expect(page.getByText('5 頁')).toBeVisible(); + + const rangeInput = page.getByLabel('要抽出的頁碼'); + await rangeInput.fill('2, 99'); + await expect(page.getByText(/頁碼格式不對或超出範圍/)).toBeVisible(); + await expect(page.getByRole('button', { name: '抽出頁面下載' })).toBeDisabled(); + + await rangeInput.fill('2, 4'); + const downloadPromise = page.waitForEvent('download'); + await page.getByRole('button', { name: '抽出頁面下載' }).click(); + const download = await downloadPromise; + expect(await downloadedPageCount((await download.path())!)).toBe(2); +}); + +test('旋轉:全部頁面轉 90° 後下載', async ({ page }) => { + await page.goto('/tools/pdf-studio'); + await waitForIslands(page); + await page.getByRole('button', { name: '旋轉' }).click(); + + await page.locator('input[type="file"]').setInputFiles({ name: 'scan.pdf', mimeType: 'application/pdf', buffer: await makePdf(2) }); + await expect(page.getByText('2 頁')).toBeVisible(); + + const downloadPromise = page.waitForEvent('download'); + await page.getByRole('button', { name: '旋轉下載' }).click(); + const download = await downloadPromise; + const doc = await PDFDocument.load(readFileSync((await download.path())!)); + expect(doc.getPage(0).getRotation().angle).toBe(90); + expect(doc.getPage(1).getRotation().angle).toBe(90); +}); diff --git a/package-lock.json b/package-lock.json index 90be3da..3e7e5cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "fit-file-parser": "^3.0.2", "jsqr": "^1.4.0", "marked": "^18.0.5", + "pdf-lib": "^1.17.1", "qrcode": "^1.5.4", "react": "^19.2.7", "react-dom": "^19.2.7", @@ -3342,6 +3343,36 @@ "url": "https://github.com/sponsors/Boshen" } }, + "node_modules/@pdf-lib/standard-fonts": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz", + "integrity": "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==", + "license": "MIT", + "dependencies": { + "pako": "^1.0.6" + } + }, + "node_modules/@pdf-lib/standard-fonts/node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, + "node_modules/@pdf-lib/upng": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@pdf-lib/upng/-/upng-1.0.1.tgz", + "integrity": "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==", + "license": "MIT", + "dependencies": { + "pako": "^1.0.10" + } + }, + "node_modules/@pdf-lib/upng/node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, "node_modules/@playwright/test": { "version": "1.61.1", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.61.1.tgz", @@ -11494,6 +11525,30 @@ "dev": true, "license": "MIT" }, + "node_modules/pdf-lib": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/pdf-lib/-/pdf-lib-1.17.1.tgz", + "integrity": "sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==", + "license": "MIT", + "dependencies": { + "@pdf-lib/standard-fonts": "^1.0.0", + "@pdf-lib/upng": "^1.0.1", + "pako": "^1.0.11", + "tslib": "^1.11.1" + } + }, + "node_modules/pdf-lib/node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, + "node_modules/pdf-lib/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, "node_modules/piccolore": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/piccolore/-/piccolore-0.1.3.tgz", diff --git a/package.json b/package.json index e486290..f82c338 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "fit-file-parser": "^3.0.2", "jsqr": "^1.4.0", "marked": "^18.0.5", + "pdf-lib": "^1.17.1", "qrcode": "^1.5.4", "react": "^19.2.7", "react-dom": "^19.2.7", diff --git a/public/og/en/pdf-studio.png b/public/og/en/pdf-studio.png new file mode 100644 index 0000000000000000000000000000000000000000..39fa4586251cb20e838513c1a0e11f6a3577ceaf GIT binary patch literal 35559 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 literal 0 HcmV?d00001 diff --git a/public/og/zh/pdf-studio.png b/public/og/zh/pdf-studio.png new file mode 100644 index 0000000000000000000000000000000000000000..3651c0c623c78a203b2a825bea524fd5e2455511 GIT binary patch literal 34184 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�g?rD{(6C4x;nQ?u=Rvk3GadWoz9cHr;aXk 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 { + const doc = await PDFDocument.create(); + for (let i = 0; i < pages; i++) { + const page = doc.addPage([200, 300]); + if (rotate) page.setRotation(degrees(rotate)); + } + return doc.save(); +} + +async function pageCount(bytes: Uint8Array): Promise { + return (await PDFDocument.load(bytes)).getPageCount(); +} + +describe('mergePdfs', () => { + it('兩份 PDF 依序合併,頁數相加', async () => { + const a = await makePdf(3); + const b = await makePdf(2); + const merged = await mergePdfs([a, b]); + expect(await pageCount(merged)).toBe(5); + }); + + it('單一檔案也能過(等於重存)', async () => { + const merged = await mergePdfs([await makePdf(4)]); + expect(await pageCount(merged)).toBe(4); + }); +}); + +describe('extractPages', () => { + it('照指定順序抽頁成新檔', async () => { + const src = await makePdf(5); + const out = await extractPages(src, [4, 2]); + expect(await pageCount(out)).toBe(2); + }); +}); + +describe('rotatePdf', () => { + it('全部頁面加轉 90°,原有旋轉角度要累加', async () => { + const src = await makePdf(2, 90); + const out = await rotatePdf(src, 90); + const doc = await PDFDocument.load(out); + expect(doc.getPage(0).getRotation().angle).toBe(180); + expect(doc.getPage(1).getRotation().angle).toBe(180); + }); + + it('只轉指定頁,其他頁不動;累加後 mod 360', async () => { + const src = await makePdf(3, 270); + const out = await rotatePdf(src, 180, [2]); + const doc = await PDFDocument.load(out); + expect(doc.getPage(0).getRotation().angle).toBe(270); + expect(doc.getPage(1).getRotation().angle).toBe(90); // 270+180 mod 360 + expect(doc.getPage(2).getRotation().angle).toBe(270); + }); +}); diff --git a/src/lib/__tests__/pdfPages.test.ts b/src/lib/__tests__/pdfPages.test.ts new file mode 100644 index 0000000..b2c985c --- /dev/null +++ b/src/lib/__tests__/pdfPages.test.ts @@ -0,0 +1,31 @@ +import { describe, expect, it } from 'vitest'; +import { parsePageRanges } from '../pdfPages'; + +describe('parsePageRanges', () => { + it('單頁、範圍、混用(1-indexed,保留輸入順序)', () => { + expect(parsePageRanges('1-3, 7, 10-12', 20)).toEqual([1, 2, 3, 7, 10, 11, 12]); + expect(parsePageRanges('5', 10)).toEqual([5]); + expect(parsePageRanges('2-2', 10)).toEqual([2]); + }); + + it('全形逗號、頓號、多餘空白都容忍(中文使用者會這樣打)', () => { + expect(parsePageRanges('1,3、5 - 6', 10)).toEqual([1, 3, 5, 6]); + }); + + it('重複頁只取一次,順序以先出現為準', () => { + expect(parsePageRanges('3, 1-4', 10)).toEqual([3, 1, 2, 4]); + }); + + it('超出總頁數、反向範圍、0 或負數 → null(整串拒絕,不猜使用者意圖)', () => { + expect(parsePageRanges('1-99', 10)).toBeNull(); + expect(parsePageRanges('5-3', 10)).toBeNull(); + expect(parsePageRanges('0', 10)).toBeNull(); + expect(parsePageRanges('-2', 10)).toBeNull(); + }); + + it('空字串或垃圾輸入 → null', () => { + expect(parsePageRanges('', 10)).toBeNull(); + expect(parsePageRanges('abc', 10)).toBeNull(); + expect(parsePageRanges('1-', 10)).toBeNull(); + }); +}); diff --git a/src/lib/pdfOps.ts b/src/lib/pdfOps.ts new file mode 100644 index 0000000..a11c348 --- /dev/null +++ b/src/lib/pdfOps.ts @@ -0,0 +1,37 @@ +// PDF 操作核心:合併/抽頁/旋轉。全部在瀏覽器(或測試的 Node)本機執行, +// 檔案不離開使用者的機器。頁碼一律 1-indexed(與 UI、parsePageRanges 一致)。 + +import { PDFDocument, degrees } from 'pdf-lib'; + +export async function mergePdfs(files: Uint8Array[]): Promise { + const out = await PDFDocument.create(); + for (const bytes of files) { + const src = await PDFDocument.load(bytes); + const pages = await out.copyPages(src, src.getPageIndices()); + for (const p of pages) out.addPage(p); + } + return out.save(); +} + +export async function extractPages(bytes: Uint8Array, pages: number[]): Promise { + const src = await PDFDocument.load(bytes); + const out = await PDFDocument.create(); + const copied = await out.copyPages(src, pages.map((p) => p - 1)); + for (const p of copied) out.addPage(p); + return out.save(); +} + +/** 指定頁(省略 = 全部)在原有旋轉上加轉 degreesToAdd(90/180/270) */ +export async function rotatePdf(bytes: Uint8Array, degreesToAdd: number, pages?: number[]): Promise { + const doc = await PDFDocument.load(bytes); + const targets = pages ?? doc.getPageIndices().map((i) => i + 1); + for (const p of targets) { + const page = doc.getPage(p - 1); + page.setRotation(degrees((page.getRotation().angle + degreesToAdd) % 360)); + } + return doc.save(); +} + +export async function pdfPageCount(bytes: Uint8Array): Promise { + return (await PDFDocument.load(bytes)).getPageCount(); +} diff --git a/src/lib/pdfPages.ts b/src/lib/pdfPages.ts new file mode 100644 index 0000000..de0371c --- /dev/null +++ b/src/lib/pdfPages.ts @@ -0,0 +1,29 @@ +// 頁碼範圍字串解析:「1-3, 7, 10-12」→ [1,2,3,7,10,11,12](1-indexed)。 +// 任一段不合法就整串回 null —— 寧可要求改正,不猜使用者意圖。 + +export function parsePageRanges(input: string, totalPages: number): number[] | null { + // 全形逗號與頓號一併當分隔符(中文輸入法很常見) + const parts = input + .replace(/[,、]/g, ',') + .split(',') + .map((s) => s.trim()) + .filter((s) => s !== ''); + if (parts.length === 0) return null; + + const seen = new Set(); + const pages: number[] = []; + for (const part of parts) { + const m = part.match(/^(\d+)(?:\s*-\s*(\d+))?$/); + if (!m) return null; + const start = Number(m[1]); + const end = m[2] !== undefined ? Number(m[2]) : start; + if (start < 1 || end < start || end > totalPages) return null; + for (let p = start; p <= end; p++) { + if (!seen.has(p)) { + seen.add(p); + pages.push(p); + } + } + } + return pages; +} diff --git a/src/pages/en/tools/pdf-studio.astro b/src/pages/en/tools/pdf-studio.astro new file mode 100644 index 0000000..25c3136 --- /dev/null +++ b/src/pages/en/tools/pdf-studio.astro @@ -0,0 +1,12 @@ +--- +import BaseLayout from '../../../layouts/BaseLayout.astro'; +import PdfStudio from '../../../tools/PdfStudio.tsx'; +import { tools } from '../../../data/tools'; + +const tool = tools.find((t) => t.id === 'pdf-studio')!; +--- + + ← All tools +

{tool.titleEn}

+ +
diff --git a/src/pages/tools/pdf-studio.astro b/src/pages/tools/pdf-studio.astro new file mode 100644 index 0000000..33e5d60 --- /dev/null +++ b/src/pages/tools/pdf-studio.astro @@ -0,0 +1,9 @@ +--- +import BaseLayout from '../../layouts/BaseLayout.astro'; +import PdfStudio from '../../tools/PdfStudio.tsx'; +--- + + ← 回到所有工具 +

PDF 工具

+ +
diff --git a/src/tools/PdfStudio.tsx b/src/tools/PdfStudio.tsx new file mode 100644 index 0000000..c3ec1fc --- /dev/null +++ b/src/tools/PdfStudio.tsx @@ -0,0 +1,280 @@ +import { useState } from 'react'; +import Tabs from '../components/Tabs'; +import { parsePageRanges } from '../lib/pdfPages'; +import { mergePdfs, extractPages, rotatePdf, pdfPageCount } from '../lib/pdfOps'; +import type { Locale } from '../lib/i18n'; + +const L = { + zh: { + tabMerge: '合併', + tabSplit: '拆分', + tabRotate: '旋轉', + addFiles: '選擇 PDF 檔案(可多選)', + addFile: '選擇 PDF 檔案', + pages: (n: number) => `${n} 頁`, + up: '上移', + down: '下移', + remove: '移除', + fileAria: (name: string) => `檔案 ${name}`, + mergeBtn: '合併下載', + mergeHint: '依清單順序合併,可用上移/下移調整。', + rangeLabel: '要抽出的頁碼', + rangePlaceholder: '例如 1-3, 7, 10-12', + rangeInvalid: '頁碼格式不對或超出範圍,請用「1-3, 7」這樣的寫法。', + splitBtn: '抽出頁面下載', + rotateAngle: '旋轉角度', + rotateRange: '頁碼(留空=全部頁面)', + rotateBtn: '旋轉下載', + working: '處理中…', + loadError: '這個檔案無法讀取,可能不是有效的 PDF 或有密碼保護。', + privacy: '所有處理都在你的瀏覽器本機完成,PDF 不會上傳到任何伺服器。', + deg: (d: number) => `${d}°`, + }, + en: { + tabMerge: 'Merge', + tabSplit: 'Split', + tabRotate: 'Rotate', + addFiles: 'Choose PDF files (multiple allowed)', + addFile: 'Choose a PDF file', + pages: (n: number) => `${n} page${n === 1 ? '' : 's'}`, + up: 'Up', + down: 'Down', + remove: 'Remove', + fileAria: (name: string) => `File ${name}`, + mergeBtn: 'Merge & download', + mergeHint: 'Merged in list order — use Up / Down to rearrange.', + rangeLabel: 'Pages to extract', + rangePlaceholder: 'e.g. 1-3, 7, 10-12', + rangeInvalid: 'Invalid page range — use the “1-3, 7” format within the page count.', + splitBtn: 'Extract & download', + rotateAngle: 'Rotation', + rotateRange: 'Pages (empty = all pages)', + rotateBtn: 'Rotate & download', + working: 'Working…', + loadError: 'Could not read this file — it may not be a valid PDF, or it is password-protected.', + privacy: 'Everything happens locally in your browser — your PDFs are never uploaded.', + deg: (d: number) => `${d}°`, + }, +} as const; +type Dict = (typeof L)[Locale]; + +interface Loaded { + name: string; + bytes: Uint8Array; + pages: number; +} + +const btn = 'rounded-lg border border-edge px-4 py-2 text-sm text-ink transition-colors hover:border-accent hover:text-accent disabled:opacity-40 disabled:hover:border-edge disabled:hover:text-ink'; +const primaryBtn = 'rounded-lg bg-accent px-4 py-2 text-sm font-medium text-white transition-opacity hover:opacity-90 disabled:opacity-40'; +const smallBtn = 'rounded border border-edge px-2 py-0.5 text-xs text-muted transition-colors hover:border-accent hover:text-accent disabled:opacity-30 disabled:hover:border-edge disabled:hover:text-muted'; +const inputClass = 'w-full rounded-lg border border-edge bg-surface px-3 py-2.5 text-ink outline-none transition-colors focus:border-accent'; + +function download(bytes: Uint8Array, filename: string) { + const a = document.createElement('a'); + a.href = URL.createObjectURL(new Blob([bytes.buffer as ArrayBuffer], { type: 'application/pdf' })); + a.download = filename; + a.click(); + URL.revokeObjectURL(a.href); +} + +async function loadFile(file: File): Promise { + const bytes = new Uint8Array(await file.arrayBuffer()); + return { name: file.name, bytes, pages: await pdfPageCount(bytes) }; +} + +function baseName(name: string): string { + return name.replace(/\.pdf$/i, ''); +} + +function FilePicker({ label, multiple, onFiles, t }: { label: string; multiple: boolean; onFiles: (files: Loaded[]) => void; t: Dict }) { + const [error, setError] = useState(''); + return ( +
+ + {error &&

{error}

} +
+ ); +} + +function MergePanel({ t }: { t: Dict }) { + const [files, setFiles] = useState([]); + const [busy, setBusy] = useState(false); + + function move(i: number, dir: -1 | 1) { + setFiles((fs) => { + const next = [...fs]; + [next[i], next[i + dir]] = [next[i + dir], next[i]]; + return next; + }); + } + + return ( +
+ setFiles((fs) => [...fs, ...f])} t={t} /> + {files.length > 0 && ( +
    + {files.map((f, i) => ( +
  • + {f.name} + {t.pages(f.pages)} + + + +
  • + ))} +
+ )} +

{t.mergeHint}

+ +
+ ); +} + +function SplitPanel({ t }: { t: Dict }) { + const [file, setFile] = useState(null); + const [range, setRange] = useState(''); + const [busy, setBusy] = useState(false); + + const pages = file && range.trim() !== '' ? parsePageRanges(range, file.pages) : null; + const invalid = file !== null && range.trim() !== '' && pages === null; + + return ( +
+ setFile(f[0])} t={t} /> + {file && ( + <> +

+ {file.name} ・ {t.pages(file.pages)} +

+ + {invalid &&

{t.rangeInvalid}

} + + + )} +
+ ); +} + +function RotatePanel({ t }: { t: Dict }) { + const [file, setFile] = useState(null); + const [angle, setAngle] = useState(90); + const [range, setRange] = useState(''); + const [busy, setBusy] = useState(false); + + const pages = file && range.trim() !== '' ? parsePageRanges(range, file.pages) : undefined; + const invalid = file !== null && range.trim() !== '' && pages === null; + + return ( +
+ setFile(f[0])} t={t} /> + {file && ( + <> +

+ {file.name} ・ {t.pages(file.pages)} +

+
+ + +
+ {invalid &&

{t.rangeInvalid}

} + + + )} +
+ ); +} + +export default function PdfStudio({ locale = 'zh' }: { locale?: Locale }) { + const t = L[locale]; + const [tab, setTab] = useState('merge'); + return ( +
+
+ +
+ {tab === 'merge' && } + {tab === 'split' && } + {tab === 'rotate' && } +

{t.privacy}

+
+ ); +}